stdp_synapse#
- class brainpy.state.stdp_synapse(weight=1.0, delay=Quantity(1., 'ms'), receptor_type=0, tau_plus=Quantity(20., 'ms'), tau_minus=Quantity(20., 'ms'), lambda_=0.01, alpha=1.0, mu_plus=1.0, mu_minus=1.0, Wmax=100.0, Kplus=0.0, post=None, name=None)#
NEST-compatible
stdp_synapseconnection model.stdp_synapseimplements pair-based spike-timing dependent plasticity (STDP) following Guetig et al. (2003) and the NEST reference implementation frommodels/stdp_synapse.h. The model supports asymmetric Hebbian learning with configurable weight-dependent potentiation and depression exponents.The synapse maintains three dynamic state variables per connection:
weight: current synaptic efficacy (plastic, updated on each presynaptic spike)Kplus: presynaptic eligibility trace (exponentially decays with time constanttau_plus)t_lastspike: timestamp of the most recent presynaptic spike
Postsynaptic spike history is stored internally with time constant
tau_minus. In NEST,tau_minusis a postsynaptic neuron parameter (ArchivingNode); here it is stored on the synapse for standalone compatibility, enabling STDP simulation without requiring postsynaptic neurons to implement archiving APIs.1. Mathematical Model
State Variables
w: Synaptic weight (plastic, bounded to \([0, W_{\max}]\) or \([W_{\max}, 0]\))K^+: Presynaptic eligibility trace (decays with \(\tau_+\))K^-: Postsynaptic eligibility trace (decays with \(\tau_-\))
Continuous-time dynamics (between spikes):
\[ \begin{align}\begin{aligned}\frac{dK^+}{dt} = -\frac{K^+}{\tau_+}\\\frac{dK^-}{dt} = -\frac{dK^-}{\tau_-}\end{aligned}\end{align} \]Upon presynaptic spike at time \(t_{\text{pre}}\):
Let \(d\) denote the dendritic (synaptic) delay. The NEST
stdp_synapse::sendmethod performs the following sequence:Step 1: Facilitation (potentiation) from past postsynaptic spikes
For each postsynaptic spike \(t_{\text{post}}\) in the window \((t_{\text{last}} - d,\, t_{\text{pre}} - d]\), where \(t_{\text{last}}\) is the timestamp of the previous presynaptic spike:
\[ \begin{align}\begin{aligned}\Delta t = (t_{\text{post}} + d) - t_{\text{last}}\\K^+_{\text{eff}} = K^+ \cdot e^{(t_{\text{last}} - (t_{\text{post}} + d)) / \tau_+}\\\hat{w} \leftarrow \hat{w} + \lambda (1 - \hat{w})^{\mu_+} K^+_{\text{eff}}\end{aligned}\end{align} \]where \(\hat{w} = w / W_{\max}\) is the normalized weight.
Step 2: Depression from current presynaptic spike
Retrieve postsynaptic trace \(K^-\) at time \(t_{\text{pre}} - d\):
\[ \begin{align}\begin{aligned}K^-_{\text{eff}} = K^-(t_{\text{pre}} - d)\\\hat{w} \leftarrow \hat{w} - \alpha \lambda \hat{w}^{\mu_-} K^-_{\text{eff}}\end{aligned}\end{align} \]Step 3: Deliver spike event
Send event with updated weight
wto the postsynaptic receiver.Step 4: Update presynaptic trace
\[ \begin{align}\begin{aligned}K^+ \leftarrow K^+ \cdot e^{(t_{\text{last}} - t_{\text{pre}}) / \tau_+} + 1\\t_{\text{last}} \leftarrow t_{\text{pre}}\end{aligned}\end{align} \]Upon postsynaptic spike at time \(t_{\text{post}}\):
\[ \begin{align}\begin{aligned}\begin{split}K^- \\leftarrow K^- \\cdot e^{(t_{\\text{last\\_post}} - t_{\\text{post}}) / \\tau_-} + 1\end{split}\\\begin{split}t_{\\text{last\\_post}} \\leftarrow t_{\\text{post}}\end{split}\end{aligned}\end{align} \]Weight Update Functions:
Potentiation (post-before-pre, \(\Delta t > 0\)):
\[\hat{w} \leftarrow \hat{w} + \lambda (1 - \hat{w})^{\mu_+} K^+\]Depression (pre-before-post, \(\Delta t < 0\)):
\[\hat{w} \leftarrow \hat{w} - \alpha \lambda \hat{w}^{\mu_-} K^-\]Final weight is clipped to \([0, W_{\max}]\) for positive weights, or \([W_{\max}, 0]\) for negative weights (inhibitory synapses).
2. Update Ordering and NEST Compatibility
This implementation replicates the exact update sequence from NEST
models/stdp_synapse.h::send():Query postsynaptic spike history in window \((t_{\text{last}} - d,\, t_{\text{pre}} - d]\)
Apply facilitation for each retrieved postsynaptic spike
Compute postsynaptic trace \(K^-\) at \(t_{\text{pre}} - d\)
Apply depression based on \(K^-\)
Schedule weighted spike event for delivery after delay \(d\)
Update presynaptic trace \(K^+\) and timestamp \(t_{\text{last}}\)
3. Event Timing Semantics
As in NEST, this model uses on-grid spike timestamps and ignores precise sub-step offsets. Spike times are discretized to simulation time steps:
Presynaptic spike detected at step
n→ stamped at \(t_{\text{spike}} = t + dt\)Postsynaptic spike recorded at step
n→ stamped at \(t_{\text{spike}} = t + dt\)Inter-spike intervals computed from discrete timestamps
This differs from continuous-time STDP but matches NEST’s default behavior.
4. Stability Constraints and Computational Implications
Parameter Constraints:
\(\tau_+ > 0\), \(\tau_- > 0\) (time constants must be positive)
\(\lambda > 0\) (learning rate; negative values would invert plasticity)
\(\alpha \geq 0\) (depression scaling; typically 1.0)
\(\mu_+ \geq 0\), \(\mu_- \geq 0\) (exponents; 1.0 for linear, 0.0 for additive)
\(W_{\max} \neq 0\) and \(\text{sign}(w) = \text{sign}(W_{\max})\)
\(K^+ \geq 0\) (trace must be non-negative)
Numerical Considerations
All state variables stored as Python
float(float64precision)Exponential decays computed using
math.exp()for numerical stabilityPower functions use
math.pow()(may degrade for large exponents)Per-spike cost: \(O(N_{\text{post}})\) where \(N_{\text{post}}\) is the number of postsynaptic spikes in the facilitation window
Behavioral Regimes:
Symmetric STDP (\(\alpha = 1\), \(\mu_+ = \mu_- = 1\)): Classical pair-based rule (Song et al., 2000)
Additive STDP (\(\mu_+ = \mu_- = 0\)): Weight-independent updates (van Rossum et al., 2000)
Multiplicative STDP (\(\mu_+ = \mu_- = 1\)): Soft bounds stabilize weight distributions (Guetig et al., 2003)
Asymmetric depression (\(\alpha > 1\)): Stronger depression relative to potentiation
Failure Modes
weightandWmaxmust have the same sign; otherwiseValueErroron init or setKplusmust be non-negative; otherwiseValueErroron init or setPostsynaptic spike history grows unbounded if not cleared; use
clear_post_history()periodically for long simulations
- Parameters:
weight (
float,array-like, orQuantity, optional) – Initial synaptic weight \(w\). Scalar float, dimensionless or with receiver-specific units (e.g., pA, nS). Must have the same sign asWmax. Default:1.0(dimensionless).delay (
float,array-like, orQuantity, optional) – Synaptic transmission delay \(d\) in milliseconds. Must be> 0. Quantized to integer time steps perstatic_synapseconventions. Default:1.0 * u.ms.receptor_type (
int, optional) – Receptor port identifier on the postsynaptic neuron. Non-negative integer specifying which input channel receives the event. Default:0.tau_plus (
float,array-like, orQuantity, optional) – Presynaptic trace time constant \(\tau_+\) in milliseconds. Must be> 0. Controls the width of the potentiation (post-before-pre) window. Default:20.0 * u.ms.tau_minus (
float,array-like, orQuantity, optional) – Postsynaptic trace time constant \(\tau_-\) in milliseconds. Must be> 0. In NEST, this is a postsynaptic neuron parameter; here it is stored on the synapse for standalone compatibility. Controls the width of the depression (pre-before-post) window. Default:20.0 * u.ms.lambda (
float,array-like, orQuantity, optional) – Learning rate parameter \(\lambda\) (dimensionless). Scales both potentiation and depression updates. Typical values: 0.001–0.1. Default:0.01.alpha (
float,array-like, orQuantity, optional) – Asymmetry parameter \(\alpha\) (dimensionless). Scales depression relative to potentiation. \(\alpha = 1.0\) yields symmetric STDP; \(\alpha > 1.0\) strengthens depression. Default:1.0.mu_plus (
float,array-like, orQuantity, optional) – Potentiation exponent \(\mu_+\) (dimensionless). Controls weight dependence of potentiation. \(\mu_+ = 0\): additive; \(\mu_+ = 1\): multiplicative (soft upper bound). Default:1.0.mu_minus (
float,array-like, orQuantity, optional) – Depression exponent \(\mu_-\) (dimensionless). Controls weight dependence of depression. \(\mu_- = 0\): additive; \(\mu_- = 1\): multiplicative (soft lower bound). Default:1.0.Wmax (
float,array-like, orQuantity, optional) – Maximum weight bound \(W_{\max}\) (same units asweight). Weights are clipped to \([0, W_{\max}]\) for excitatory synapses or \([W_{\max}, 0]\) for inhibitory synapses. Must have the same sign asweight. Default:100.0(dimensionless).Kplus (
float,array-like, orQuantity, optional) – Initial presynaptic trace value \(K^+\) (dimensionless). Must be non-negative. Typically initialized to0.0(no presynaptic history). Default:0.0.post (
Dynamics, optional) – Default postsynaptic receiver object. If provided,send()andupdate()will target this receiver unless overridden. Must implement eitheradd_delta_inputoradd_current_inputmethods. Default:None(must provide receiver explicitly in method calls).name (
str, optional) – Unique identifier for this synapse instance. Default: auto-generated.
Parameter Mapping
NEST
stdp_synapseparameters map to this implementation as follows:NEST Parameter
brainpy.state Param
Notes
weightweightPlastic, updated on each pre-spike
delaydelayConverted to ms, discretized to steps
receptor_typereceptor_typeInteger ≥ 0
tau_plustau_plusPre-synaptic trace decay (ms)
tau_minus(neuron param)
Here: synapse param
tau_minus(ms)lambdalambda_Learning rate (underscore to avoid keyword)
alphaalphaDepression asymmetry factor
mu_plusmu_plusPotentiation exponent
mu_minusmu_minusDepression exponent
WmaxWmaxWeight upper bound (or lower for inhib.)
KplusKplusPre-synaptic trace state variable
See also
static_synapseBase class for non-plastic synapses
tsodyks_synapseShort-term plasticity (depression/facilitation)
stdp_synapse_homHomogeneous-weight variant with shared weight across connections
Notes
The model transmits spike-like events only (
event_type='spike').update(pre_spike=..., post_spike=...)accepts both presynaptic and postsynaptic spike multiplicities for standalone STDP simulation.record_post_spike(...)can be used to manually feed postsynaptic spikes when the postsynaptic model does not expose NEST archiving APIs.Postsynaptic spike history grows unbounded; call
clear_post_history()periodically in long simulations to prevent memory issues.
References
Examples
Basic STDP synapse with default parameters:
>>> import brainpy.state as bst >>> import saiunit as u >>> syn = bst.stdp_synapse(weight=0.5, delay=1.0 * u.ms) >>> syn.get() {'weight': 0.5, 'delay': 1.0, 'receptor_type': 0, 'tau_plus': 20.0, 'tau_minus': 20.0, 'lambda': 0.01, 'alpha': 1.0, 'mu_plus': 1.0, 'mu_minus': 1.0, 'Wmax': 100.0, 'Kplus': 0.0, 'synapse_model': 'stdp_synapse'}
Asymmetric STDP (stronger depression):
>>> syn = bst.stdp_synapse( ... weight=1.0, ... tau_plus=16.8 * u.ms, ... tau_minus=33.7 * u.ms, ... lambda_=0.005, ... alpha=1.05, # 5% stronger depression ... Wmax=2.0, ... )
Additive STDP (weight-independent updates):
>>> syn = bst.stdp_synapse( ... weight=0.5, ... mu_plus=0.0, # additive potentiation ... mu_minus=0.0, # additive depression ... lambda_=0.001, ... Wmax=1.0, ... )
Manual postsynaptic spike recording:
>>> import brainstate >>> with brainstate.environ.context(dt=0.1 * u.ms): ... syn = bst.stdp_synapse(weight=1.0) ... syn.init_state() ... # Simulate postsynaptic spike at t=5.0 ms ... syn.record_post_spike(multiplicity=1, t_spike_ms=5.0) ... # Simulate presynaptic spike at t=10.0 ms (after post-spike) ... # This should potentiate the weight ... syn.send(multiplicity=1) # uses on-grid stamp t + dt ... print(f"Updated weight: {syn.weight:.6f}") # > 1.0 (potentiated)
- clear_post_history()[source]#
Clear internal postsynaptic spike history and reset trace state.
Resets all postsynaptic STDP state to initial conditions:
Clears spike history buffer (timestamps and trace values)
Resets postsynaptic trace
K^-to zeroResets last postsynaptic spike timestamp to
-1.0
This method should be called periodically in long simulations to prevent unbounded growth of the spike history buffer. Typical usage: clear history at the start of each trial or after weight convergence phases.
See also
init_stateReinitialize all synapse state including weights and traces
record_post_spikeRecord postsynaptic spikes into the history buffer
- get()[source]#
Return current public parameters and mutable state.
Retrieves all NEST-compatible parameters and dynamic state variables in a dictionary format suitable for inspection, logging, or serialization. Includes both inherited parameters from
static_synapse(weight,delay,receptor_type) and STDP-specific parameters.- Returns:
Dictionary containing:
'weight': float – Current synaptic weight (plastic)'delay': float – Transmission delay (ms)'receptor_type': int – Postsynaptic receptor port'tau_plus': float – Presynaptic trace time constant (ms)'tau_minus': float – Postsynaptic trace time constant (ms)'lambda': float – Learning rate (key name without underscore)'alpha': float – Depression asymmetry factor'mu_plus': float – Potentiation exponent'mu_minus': float – Depression exponent'Wmax': float – Maximum weight bound'Kplus': float – Current presynaptic trace value'synapse_model': str – Always'stdp_synapse'(NEST identifier)
- Return type:
See also
setUpdate parameters and state
init_stateReinitialize state to defaults
Examples
>>> syn = bst.stdp_synapse(weight=0.5, lambda_=0.01) >>> params = syn.get() >>> params['weight'] 0.5 >>> params['lambda'] 0.01 >>> params['synapse_model'] 'stdp_synapse'
- init_state(batch_size=None, **kwargs)[source]#
Initialize synapse state for simulation.
Resets all dynamic state variables to their initial values:
Kplus: presynaptic trace → initial value (default0.0)t_lastspike: last presynaptic spike time →0.0msPostsynaptic spike history and trace → cleared
This method should be called before starting a new simulation or trial. Inherits delay queue initialization from
static_synapse.- Parameters:
batch_size (
int, optional) – Ignored (provided for API compatibility with batched models).**kwargs – Ignored (provided for API compatibility).
See also
clear_post_historyClear only postsynaptic history without resetting other state
setUpdate parameters without reinitializing state
- record_post_spike(multiplicity=1.0, *, t_spike_ms=None)[source]#
Record postsynaptic spikes into the internal STDP history buffer.
This method updates the postsynaptic eligibility trace \(K^-\) and stores the spike timestamp for later use by
send()when processing presynaptic spikes. Each recorded spike increments \(K^-\) by 1.0 after exponential decay from the previous postsynaptic spike.The trace update follows:
\[\begin{split}K^- \\leftarrow K^- \\cdot e^{(t_{\\text{last\\_post}} - t_{\\text{spike}}) / \\tau_-} + 1\end{split}\]where \(t_{\\text{last\\_post}}\) is the timestamp of the previous postsynaptic spike and \(\\tau_-\) is the postsynaptic trace time constant.
Multiple spikes can be recorded by setting
multiplicity > 1. This is equivalent to calling the methodmultiplicitytimes at the same timestamp.- Parameters:
multiplicity (
float,array-like, orQuantity, optional) – Number of postsynaptic spikes to record at this timestamp. Must be a non-negative integer-valued scalar (fractional values will be rejected). Usemultiplicity=0to skip recording (returns immediately). Default:1(single spike).t_spike_ms (
float,array-like, orQuantity, optional) – Spike timestamp in milliseconds. Must be a scalar float with or without time units. IfNone, uses the current on-grid spike stamp \(t + dt\) where \(t\) is the current simulation time and \(dt\) is the simulation time step. Default:None(on-grid).
- Returns:
Number of spikes successfully recorded (equal to
multiplicity).- Return type:
- Raises:
If
multiplicityis negative or not integer-valued. - Ift_spike_msis not a finite scalar.
See also
clear_post_historyClear all postsynaptic spike history
sendProcess presynaptic spike and apply STDP weight updates
Examples
Record single postsynaptic spike at current time:
>>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... syn = bst.stdp_synapse(weight=1.0) ... syn.init_state() ... count = syn.record_post_spike() # uses t + dt ... print(count) 1
Record postsynaptic spike at explicit timestamp:
>>> syn.record_post_spike(multiplicity=1, t_spike_ms=5.0) 1
Record burst of 3 postsynaptic spikes:
>>> syn.record_post_spike(multiplicity=3) 3
- send(multiplicity=1.0, *, post=None, receptor_type=None)[source]#
Schedule one outgoing event with NEST
stdp_synapsedynamics.Processes a presynaptic spike, applies STDP weight updates (facilitation from past postsynaptic spikes and depression from current spike), then schedules the event for delayed delivery to the postsynaptic receiver. This method replicates the exact update sequence from NEST
models/stdp_synapse.h::send().Update sequence:
Compute inter-spike interval \(h = t_{\text{spike}} - t_{\text{last}}\)
Retrieve postsynaptic spikes in window \((t_{\\text{last}} - d,\\, t_{\\text{spike}} - d]\)
Apply facilitation for each retrieved postsynaptic spike (post-before-pre)
Compute postsynaptic trace \(K^-\) at \(t_{\text{spike}} - d\)
Apply depression based on \(K^-\) (pre-before-post)
Schedule weighted event for delivery at \(t_{\text{spike}} + \text{delay}\)
Update presynaptic trace: \(K^+ \\leftarrow K^+ \\cdot e^{-h/\\tau_+} + 1\)
Update last spike timestamp: \(t_{\\text{last}} \\leftarrow t_{\\text{spike}}\)
The final delivered weight is \(w_{\\text{eff}} = w \\cdot \\text{multiplicity}\) where \(w\) is the plasticity-updated weight.
- Parameters:
multiplicity (
float,array-like, orQuantity, optional) – Presynaptic spike multiplicity (event magnitude). Scalar value, typically1.0for a single spike or0.0to skip transmission. The delivered payload is scaled by this factor. Default:1.0.post (
Dynamics, optional) – Postsynaptic receiver object for this event. IfNone, uses the default receiver specified during initialization. Must implementadd_delta_inputor handle spike events. Default:None(use default receiver).receptor_type (
int, optional) – Receptor port override for this event. IfNone, uses the synapse’s defaultreceptor_type. Default:None(use default receptor).
- Returns:
Trueif an event was scheduled (multiplicity != 0),Falseotherwise.- Return type:
- Raises:
If
multiplicityis not a finite scalar. - Ifreceptor_typeis negative or not an integer. - If no postsynaptic receiver is available (neitherpostargument nor default receiver specified).
See also
updateHigh-level method combining event delivery, post-spike recording, and sending
record_post_spikeManually record postsynaptic spikes for STDP
Notes
Spike timestamp uses on-grid time \(t + dt\) (NEST convention).
Dendritic delay \(d\) shifts the STDP causality window backward in time.
Postsynaptic spike history is never cleared by this method; call
clear_post_history()periodically to prevent memory growth.
Examples
Send single presynaptic spike:
>>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... post_neuron = bst.LIF(1) ... syn = bst.stdp_synapse(weight=1.0, post=post_neuron) ... syn.init_state() ... post_neuron.init_state() ... success = syn.send(multiplicity=1.0) ... print(success) True
Send presynaptic spike with receptor override:
>>> syn.send(multiplicity=1.0, receptor_type=1) # target receptor port 1 True
Skip transmission (zero multiplicity):
>>> syn.send(multiplicity=0.0) False
- set(*, weight=<object object>, delay=<object object>, receptor_type=<object object>, tau_plus=<object object>, tau_minus=<object object>, lambda_=<object object>, alpha=<object object>, mu_plus=<object object>, mu_minus=<object object>, Wmax=<object object>, Kplus=<object object>, post=<object object>)[source]#
Set NEST-style public parameters and mutable state.
Updates one or more synapse parameters and state variables without reinitializing the full simulation state. Mimics NEST’s
SetStatusAPI. All parameters are validated before assignment to ensure consistency (e.g.,weightandWmaxmust have the same sign).Only specified parameters are updated; unspecified parameters retain their current values. To reset all state to initial conditions, use
init_state()instead.- Parameters:
weight (
float,array-like, orQuantity, optional) – New synaptic weight. Must have the same sign asWmax(or newWmaxif both are specified). Validated before assignment. Default: unchanged.delay (
float,array-like, orQuantity, optional) – New transmission delay (ms). Must be positive. Will be discretized to integer time steps on next usage. Default: unchanged.receptor_type (
int, optional) – New receptor port identifier. Must be non-negative. Default: unchanged.tau_plus (
float,array-like, orQuantity, optional) – New presynaptic trace time constant (ms). Must be positive. Default: unchanged.tau_minus (
float,array-like, orQuantity, optional) – New postsynaptic trace time constant (ms). Must be positive. Default: unchanged.lambda (
float,array-like, orQuantity, optional) – New learning rate. Typically positive. Default: unchanged.alpha (
float,array-like, orQuantity, optional) – New depression asymmetry factor. Typically non-negative. Default: unchanged.mu_plus (
float,array-like, orQuantity, optional) – New potentiation exponent. Must be non-negative. Default: unchanged.mu_minus (
float,array-like, orQuantity, optional) – New depression exponent. Must be non-negative. Default: unchanged.Wmax (
float,array-like, orQuantity, optional) – New maximum weight bound. Must have the same sign asweight(or newweightif both are specified). Default: unchanged.Kplus (
float,array-like, orQuantity, optional) – New presynaptic trace value. Must be non-negative. Typically used to restore saved state rather than manipulate during simulation. Default: unchanged.post (
Dynamics, optional) – New default postsynaptic receiver. Default: unchanged.
- Raises:
If
weightandWmaxhave different signs. - IfKplusis negative. - If any parameter has non-finite values or incorrect shape.
See also
getRetrieve current parameters and state
init_stateReinitialize all state to defaults
Examples
Update learning rate during simulation:
>>> syn = bst.stdp_synapse(weight=1.0, lambda_=0.01) >>> syn.set(lambda_=0.001) # reduce learning rate >>> syn.get()['lambda'] 0.001
Update multiple parameters atomically:
>>> syn.set( ... weight=0.5, ... Wmax=2.0, ... alpha=1.1, ... )
Restore saved state:
>>> saved_params = syn.get() >>> # ... simulation ... >>> syn.set(**{k: v for k, v in saved_params.items() ... if k != 'synapse_model'}) # restore all except model name
- update(pre_spike=0.0, *, post_spike=0.0, post=None, receptor_type=None)[source]#
Deliver due events, update post history, then process pre spikes.
High-level update method combining all STDP synapse operations for a single simulation time step. This method is typically called once per time step in network simulations and handles:
Delivery of delayed events from previous time steps
Recording of postsynaptic spikes into the STDP history buffer
Aggregation of presynaptic inputs (from
current_inputsanddelta_inputs)STDP weight update and event scheduling via
send()
The update order ensures correct causality: delayed events are delivered before processing new spikes, and postsynaptic spikes are recorded before presynaptic spikes are processed (allowing immediate STDP updates if delay is minimal).
Update sequence:
- Step 1: Deliver due events
Check the internal delay queue and deliver all events scheduled for the current simulation step to their target receivers.
- Step 2: Record postsynaptic spikes
If
post_spike > 0, recordpost_spikepostsynaptic spikes at timestamp \(t + dt\) into the STDP history buffer. This updates the postsynaptic trace \(K^-\).- Step 3: Aggregate presynaptic inputs
Sum inputs from: -
pre_spikeargument (explicit input) -current_inputsdict (accumulated continuous inputs) -delta_inputsdict (accumulated spike inputs)- Step 4: Process presynaptic spike
If aggregated input is non-zero, call
send()to apply STDP weight updates and schedule a new delayed event.
- Parameters:
pre_spike (
float,array-like, orQuantity, optional) – Presynaptic spike multiplicity (explicit input). Added to accumulated inputs fromcurrent_inputsanddelta_inputs. Typically0.0(no explicit input) or1.0(single spike). Default:0.0.post_spike (
float,array-like, orQuantity, optional) – Postsynaptic spike multiplicity to record. Must be a non-negative integer-valued scalar. If> 0, records the specified number of postsynaptic spikes at the current on-grid timestamp \(t + dt\). Default:0.0(no postsynaptic spikes).post (
Dynamics, optional) – Postsynaptic receiver object for event delivery. IfNone, uses the default receiver specified during initialization. Default:None.receptor_type (
int, optional) – Receptor port override for event delivery. IfNone, uses the synapse’s defaultreceptor_type. Default:None.
- Returns:
Number of events delivered during this time step (from the delay queue). Does not include the newly scheduled event from this time step’s presynaptic spike (that event will be counted in a future time step).
- Return type:
- Raises:
If
post_spikeis negative or not integer-valued. - Ifpre_spikeor aggregated inputs are not finite scalars.
See also
sendLow-level method for processing a single presynaptic spike
record_post_spikeRecord postsynaptic spikes without other update operations
Notes
This method modifies synapse state (
weight,Kplus,t_lastspike, postsynaptic history) and should be called exactly once per time step.The returned delivery count reflects past events, not the current time step’s transmission.
For standalone STDP testing without a network, manually call
record_post_spike()andsend()instead of relying onupdate().
Examples
Typical usage in network simulation loop:
>>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... pre = bst.LIF(1) ... post = bst.LIF(1) ... syn = bst.stdp_synapse(weight=1.0, post=post) ... pre.init_state() ... post.init_state() ... syn.init_state() ... # Simulation step: presynaptic spike, no postsynaptic spike ... delivered = syn.update(pre_spike=1.0, post_spike=0.0) ... # Simulation step: no presynaptic spike, postsynaptic spike ... delivered = syn.update(pre_spike=0.0, post_spike=1.0)
Standalone STDP test with explicit spike times:
>>> with brainstate.environ.context(dt=0.1 * u.ms): ... syn = bst.stdp_synapse(weight=1.0, tau_plus=20.0*u.ms, tau_minus=20.0*u.ms) ... syn.init_state() ... # Post-before-pre: potentiation expected ... syn.record_post_spike(multiplicity=1, t_spike_ms=5.0) ... syn.send(multiplicity=1) # pre-spike at t + dt (uses on-grid time) ... print(f"Weight after potentiation: {syn.weight:.6f}") # > 1.0