stdp_dopamine_synapse#

class brainpy.state.stdp_dopamine_synapse(weight=1.0, delay=Quantity(1., 'ms'), receptor_type=0, volume_transmitter=None, A_plus=1.0, A_minus=1.5, tau_plus=Quantity(20., 'ms'), tau_minus=Quantity(20., 'ms'), tau_c=Quantity(1000., 'ms'), tau_n=Quantity(200., 'ms'), b=0.0, Wmin=0.0, Wmax=200.0, Kplus=0.0, c=0.0, n=0.0, post=None, name=None)#

NEST-compatible stdp_dopamine_synapse connection model.

Synapse type for dopamine-modulated spike-timing dependent plasticity following the NEST models/stdp_dopamine_synapse implementation. This model combines classical STDP with reward modulation through an eligibility trace mechanism, enabling reinforcement learning in spiking neural networks.

1. Model Overview

stdp_dopamine_synapse implements dopamine-modulated STDP with per-connection state variables:

  • weight – synaptic efficacy (modulated by dopamine)

  • Kplus – presynaptic facilitation trace

  • c – eligibility trace (records recent spike timing correlations)

  • n – dopamine trace (reward signal)

  • t_last_update – timestamp of last propagated state update

  • t_lastspike – timestamp of previous presynaptic spike

In NEST, the postsynaptic depression trace Kminus is read from the postsynaptic archiving neuron. For standalone compatibility, this implementation maintains an internal post-spike history buffer parameterized by tau_minus (not a synapse parameter in NEST but a neuron property).

Dopaminergic spikes are provided by a NEST volume_transmitter device. In this implementation, dopamine spikes can be fed through update(..., dopa_spike=...) or record_dopa_spike(), while still requiring a non-None volume_transmitter handle to preserve NEST connection semantics.

2. Mathematical Formulation

Between spike events, the model integrates three coupled differential equations:

\[\begin{split}\frac{dw}{dt} &= c(t) \cdot (n(t) - b) \\ \frac{dc}{dt} &= -\frac{c}{\tau_c} \\ \frac{dn}{dt} &= -\frac{n}{\tau_n}\end{split}\]

where:

  • \(w\) – synaptic weight

  • \(c\) – eligibility trace

  • \(n\) – dopamine concentration

  • \(b\) – dopamine baseline

  • \(\tau_c\) – eligibility trace decay time constant

  • \(\tau_n\) – dopamine trace decay time constant

Weight updates are computed analytically using expm1 over each time interval:

\[w \leftarrow w - c_0 \left( \frac{n_0}{\tau_s} \cdot \mathrm{expm1}(\tau_s \Delta t) - b \tau_c \cdot \mathrm{expm1}\left(\frac{\Delta t}{\tau_c}\right) \right)\]

where \(\tau_s = \frac{\tau_c + \tau_n}{\tau_c \tau_n}\) and \(\Delta t = t_0 - t_1 \le 0\). The weight is then clipped to \([W_{\min}, W_{\max}]\).

3. Eligibility Trace Updates

At spike events, the eligibility trace is modified by:

  • Facilitation (pre-before-post): \(c \leftarrow c + A_+ K_+(t)\)

  • Depression (post-before-pre): \(c \leftarrow c - A_- K_-(t)\)

where \(K_+\) and \(K_-\) are exponentially decaying spike traces:

\[\begin{split}K_+(t) &= \sum_{t_i^{\mathrm{pre}} < t} \exp\left(-\frac{t - t_i^{\mathrm{pre}}}{\tau_+}\right) \\ K_-(t) &= \sum_{t_j^{\mathrm{post}} < t} \exp\left(-\frac{t - t_j^{\mathrm{post}}}{\tau_-}\right)\end{split}\]

4. Update Order for Presynaptic Spikes

For a presynaptic spike at time \(t_{\mathrm{pre}}\) with dendritic delay \(d\), NEST stdp_dopamine_synapse::send performs:

  1. Read postsynaptic history in \((t_{\mathrm{last\_update}} - d,\; t_{\mathrm{pre}} - d]\)

  2. For each postsynaptic spike \(t_{\mathrm{post}}\) in that range:

    1. Propagate dopamine/eligibility/weight to \(t_{\mathrm{post}} + d\)

    2. If \(t_{\mathrm{pre}} - t_{\mathrm{post}} > \epsilon\), facilitate: \(c \leftarrow c + A_+ K_+ \exp((t_{\mathrm{last\_update}} - (t_{\mathrm{post}} + d)) / \tau_+)\)

  3. Propagate dopamine/eligibility/weight to \(t_{\mathrm{pre}}\)

  4. Depress eligibility: \(c \leftarrow c - A_- K_-(t_{\mathrm{pre}} - d)\)

  5. Send event with updated weight

  6. Update presynaptic trace: \(K_+ \leftarrow K_+ \exp((t_{\mathrm{last\_update}} - t_{\mathrm{pre}}) / \tau_+) + 1\)

  7. Set \(t_{\mathrm{last\_update}} = t_{\mathrm{lastspike}} = t_{\mathrm{pre}}\)

This implementation preserves the exact NEST update ordering.

5. Event Timing Semantics

As in NEST, this model uses on-grid spike timestamps and ignores precise sub-step offsets for plasticity updates. All spike times are rounded to simulation time steps.

Parameters:
  • weight (float or array-like, optional) – Initial synaptic weight (unitless). Default: 1.0.

  • delay (Quantity or array-like, optional) – Synaptic delay. Must have time units. Default: 1.0 * u.ms.

  • receptor_type (int, optional) – Receiver port/receptor identifier on the postsynaptic neuron. Default: 0.

  • volume_transmitter (object or None, optional) – Handle to a dopamine volume transmitter (NEST compatibility placeholder). Must be set to a non-None value before simulation starts. Default: None.

  • A_plus (float or array-like, optional) – Facilitation amplitude for pre-before-post spike pairs (unitless). Default: 1.0.

  • A_minus (float or array-like, optional) – Depression amplitude for post-before-pre spike pairs (unitless). Default: 1.5.

  • tau_plus (Quantity or array-like, optional) – Presynaptic trace decay time constant. Must have time units. Default: 20.0 * u.ms.

  • tau_minus (Quantity or array-like, optional) – Postsynaptic trace decay time constant. Must have time units. In NEST, this is a neuron property; here it is stored on the synapse for standalone operation. Default: 20.0 * u.ms.

  • tau_c (Quantity or array-like, optional) – Eligibility trace decay time constant. Must have time units. Default: 1000.0 * u.ms.

  • tau_n (Quantity or array-like, optional) – Dopamine trace decay time constant. Must have time units. Default: 200.0 * u.ms.

  • b (float or array-like, optional) – Dopamine baseline concentration (unitless). Weight changes occur proportionally to \((n - b)\). Default: 0.0.

  • Wmin (float or array-like, optional) – Minimum allowed weight (hard lower bound). Default: 0.0.

  • Wmax (float or array-like, optional) – Maximum allowed weight (hard upper bound). Default: 200.0.

  • Kplus (float or array-like, optional) – Initial presynaptic trace value. Must be non-negative. Default: 0.0.

  • c (float or array-like, optional) – Initial eligibility trace value. Default: 0.0.

  • n (float or array-like, optional) – Initial dopamine trace value. Default: 0.0.

  • post (Dynamics or None, optional) – Default postsynaptic receiver object. Can be overridden in send(). Default: None.

  • name (str or None, optional) – Object name for identification. Default: None.

Parameter Mapping

NEST-to-BrainPy parameter correspondence:

NEST Parameter

BrainPy Parameter

Units

Description

weight

weight

unitless

Synaptic efficacy

delay

delay

ms

Synaptic delay

receptor_type

receptor_type

integer

Postsynaptic receptor port

vt

volume_transmitter

handle

Dopamine volume transmitter

A_plus

A_plus

unitless

Facilitation amplitude

A_minus

A_minus

unitless

Depression amplitude

tau_plus

tau_plus

ms

Pre-trace time constant

(neuron property)

tau_minus

ms

Post-trace time constant

tau_c

tau_c

ms

Eligibility decay constant

tau_n

tau_n

ms

Dopamine decay constant

b

b

unitless

Dopamine baseline

Wmin

Wmin

unitless

Minimum weight bound

Wmax

Wmax

unitless

Maximum weight bound

Kplus

Kplus

unitless

Presynaptic trace state

c

c

unitless

Eligibility trace state

n

n

unitless

Dopamine trace state

Raises:

ValueError

  • If any time constant (tau_plus, tau_minus, tau_c, tau_n) is non-positive. - If Kplus is negative. - If volume_transmitter is None when send or update is called. - If dopamine spikes are recorded out of temporal order. - If trigger_update_weight is called with a time earlier than t_last_update. - If any scalar parameter has non-scalar shape or non-finite value.

Notes

  • This model transmits spike-like events only (no continuous current injection).

  • update(pre_spike=..., post_spike=..., dopa_spike=...) supports explicit per-step dopamine multiplicity for standalone simulations without a separate volume transmitter object.

  • One trigger_update_weight propagation is performed per simulation step in update(), corresponding to NEST’s default volume_transmitter delivery interval.

  • Common properties (A_plus, A_minus, tau_plus, tau_c, tau_n, Wmin, Wmax, b, volume_transmitter) cannot be specified in connect-time synapse specs; set them on the model template (via CopyModel/SetDefaults in NEST parlance).

  • For large-scale simulations, consider memory overhead: each synapse maintains internal postsynaptic and dopamine spike histories.

Examples

Basic usage with explicit dopamine signaling:

>>> import brainpy.state as bp
>>> import saiunit as u
>>> # Create synapse with volume transmitter placeholder
>>> syn = bp.stdp_dopamine_synapse(
...     weight=1.0,
...     delay=1.0 * u.ms,
...     A_plus=1.0,
...     A_minus=1.5,
...     tau_plus=20.0 * u.ms,
...     tau_minus=20.0 * u.ms,
...     tau_c=1000.0 * u.ms,
...     tau_n=200.0 * u.ms,
...     b=0.0,
...     Wmin=0.0,
...     Wmax=10.0,
...     volume_transmitter=object()  # dummy handle
... )
>>> syn.init_state()
>>> # Simulate pre spike followed by post spike (causal pairing)
>>> syn.update(pre_spike=1.0, post_spike=0.0)  # pre spike at t=dt
>>> syn.update(pre_spike=0.0, post_spike=1.0)  # post spike at t=2*dt
>>> # Deliver reward signal
>>> syn.update(pre_spike=0.0, post_spike=0.0, dopa_spike=1.0)  # dopamine at t=3*dt
>>> print(f"Weight after reward: {syn.weight:.3f}")
Weight after reward: ...

Recording dopamine spikes explicitly:

>>> syn = bp.stdp_dopamine_synapse(
...     weight=5.0,
...     volume_transmitter=object(),
...     tau_c=1000.0 * u.ms,
...     tau_n=200.0 * u.ms
... )
>>> syn.init_state()
>>> # Record multiple dopamine spikes
>>> syn.record_dopa_spike(1.0, t_spike_ms=10.0)
1.0
>>> syn.record_dopa_spike(0.5, t_spike_ms=15.0)
0.5
>>> # Trigger weight update to specified time
>>> syn.trigger_update_weight(t_trig_ms=20.0)
>>> print(f"Weight: {syn.weight:.3f}")
Weight: ...

See also

static_synapse

Base class for static synaptic connections

stdp_synapse

Classical spike-timing dependent plasticity without dopamine modulation

tsodyks_synapse

Short-term synaptic plasticity (depression and facilitation)

References

check_synapse_params(syn_spec)[source]#

Validate connect-time synapse specification for disallowed common properties.

Enforces NEST convention that certain parameters (STDP learning rules, dopamine modulation parameters) must be set on the synapse model template rather than per-connection at connect time.

Parameters:

syn_spec (dict or None) – Synapse specification dictionary to validate. If None, no validation is performed.

Raises:

ValueError – If syn_spec contains any of the following disallowed keys: 'vt', 'volume_transmitter', 'A_plus', 'A_minus', 'tau_plus', 'tau_c', 'tau_n', 'Wmin', 'Wmax', 'b'.

Notes

In NEST, dopamine synapse common properties are set globally on the synapse model (via CopyModel or SetDefaults), not per-connection. This method enforces that convention to prevent user confusion and maintain NEST compatibility.

Allowed per-connection parameters: weight, delay, receptor_type.

Examples

>>> syn = bp.stdp_dopamine_synapse(volume_transmitter=object())
>>> # Valid: per-connection weight/delay
>>> syn.check_synapse_params({'weight': 2.0, 'delay': 1.5})
>>> # Invalid: common property at connect time
>>> syn.check_synapse_params({'A_plus': 1.0})
Traceback (most recent call last):
    ...
ValueError: A_plus cannot be specified in connect-time synapse parameters ...
clear_dopamine_history()[source]#

Reset internal dopamine spike history.

Clears the dopamine spike buffer and reinitializes it with a single pseudo-spike at the current t_last_update time with zero multiplicity. This effectively resets the dopamine delivery history while preserving temporal continuity.

Notes

Does not modify the current dopamine trace value (n). To reset n as well, use set() with n=0.0 or call init_state().

clear_post_history()[source]#

Clear internal postsynaptic STDP history state.

Resets the postsynaptic spike history buffer and depression trace to initial conditions. This is useful when reinitializing the synapse or starting a new trial in an experiment.

Notes

Does not affect presynaptic traces (Kplus), eligibility trace (c), dopamine trace (n), or weight. To fully reset the synapse, use init_state() instead.

get()[source]#

Return current public parameters and mutable state.

Retrieves all NEST-style synapse parameters and state variables as a dictionary, suitable for inspection, serialization, or comparison with NEST GetStatus output.

Returns:

Dictionary containing:

  • All parent class parameters (weight, delay, receptor_type, etc.)

  • volume_transmitter: dopamine volume transmitter handle

  • A_plus: facilitation amplitude

  • A_minus: depression amplitude

  • tau_plus: presynaptic trace time constant (ms)

  • tau_minus: postsynaptic trace time constant (ms)

  • tau_c: eligibility trace time constant (ms)

  • tau_n: dopamine trace time constant (ms)

  • b: dopamine baseline

  • Wmin: minimum weight bound

  • Wmax: maximum weight bound

  • Kplus: current presynaptic trace value

  • c: current eligibility trace value

  • n: current dopamine trace value

  • synapse_model: model identifier ('stdp_dopamine_synapse')

Return type:

dict

Notes

Time constants are returned in milliseconds (internal representation), not as saiunit.Quantity objects.

Examples

>>> syn = bp.stdp_dopamine_synapse(
...     weight=5.0,
...     A_plus=1.0,
...     volume_transmitter=object()
... )
>>> syn.init_state()
>>> params = syn.get()
>>> print(params['weight'])
5.0
>>> print(params['A_plus'])
1.0
>>> print(params['synapse_model'])
stdp_dopamine_synapse
init_state(batch_size=None, **kwargs)[source]#

Initialize or reset all synapse state variables to their default values.

Resets all dynamic state (traces, timestamps, spike histories) to initial conditions as specified during construction or via set(). This is typically called at the start of a simulation.

Parameters:
  • batch_size (int or None, optional) – Ignored (included for API compatibility). This synapse operates in scalar mode.

  • **kwargs – Additional keyword arguments (ignored).

Notes

Resets the following state variables:

  • Kplus: presynaptic trace

  • c: eligibility trace

  • n: dopamine trace

  • t_last_update: last update timestamp

  • t_lastspike: last presynaptic spike timestamp

  • Postsynaptic spike history (cleared)

  • Dopamine spike history (initialized with zero-multiplicity pseudo-spike)

  • Event delivery queue (inherited from parent)

Does not reset parameters (A_plus, tau_c, etc.) or initial weight.

record_dopa_spike(multiplicity=1.0, *, t_spike_ms=None)[source]#

Record dopamine spikes into internal volume-transmitter history.

Adds dopamine delivery events to the internal dopamine spike buffer. These events are processed during subsequent trigger_update_weight() calls to update the dopamine trace (n) and modulate synaptic weight changes.

Parameters:
  • multiplicity (float or array-like, optional) – Dopamine spike magnitude (must be non-negative float). Unlike postsynaptic spikes, dopamine “spikes” can have arbitrary positive real values representing dopamine concentration increments. Default: 1.0.

  • t_spike_ms (float, array-like, or None, optional) – Explicit timestamp in milliseconds for dopamine delivery. If None, uses current simulation time plus one timestep (t + dt). Default: None.

Returns:

Dopamine multiplicity actually recorded.

Return type:

float

Raises:
  • ValueError – If multiplicity is negative.

  • ValueError – If t_spike_ms is earlier than the last recorded dopamine spike time (dopamine spikes must be recorded in non-decreasing temporal order).

  • ValueError – If multiplicity or t_spike_ms is not scalar or not finite.

Notes

  • Multiple dopamine deliveries at the same timestamp (within tolerance \(\epsilon = 10^{-6}\) ms) are accumulated additively.

  • Dopamine spikes are not processed immediately; they are stored in a queue and integrated during the next trigger_update_weight() call.

  • To simulate volume transmission delays, record dopamine spikes at future timestamps relative to the triggering event.

Examples

>>> syn = bp.stdp_dopamine_synapse(
...     volume_transmitter=object(),
...     tau_n=200.0 * u.ms
... )
>>> syn.init_state()
>>> # Record reward signal at t=100 ms
>>> mult = syn.record_dopa_spike(1.5, t_spike_ms=100.0)
>>> print(f"Recorded dopamine: {mult}")
Recorded dopamine: 1.5
>>> # Accumulate additional dopamine at same time
>>> mult = syn.record_dopa_spike(0.5, t_spike_ms=100.0)
>>> print(f"Total at t=100: {syn._dopa_spikes[-1][1]}")
Total at t=100: 2.0
>>> # Trigger update to integrate dopamine
>>> syn.trigger_update_weight(t_trig_ms=150.0)
record_post_spike(multiplicity=1.0, *, t_spike_ms=None)[source]#

Record postsynaptic spikes into internal STDP history.

Adds postsynaptic spike events to the internal history buffer and updates the postsynaptic depression trace (Kminus). These spikes are used during subsequent presynaptic spike processing to compute eligibility trace updates.

Parameters:
  • multiplicity (float or array-like, optional) – Number of spikes to record (must be non-negative integer or convertible to integer). Fractional values will be rounded. Default: 1.0.

  • t_spike_ms (float, array-like, or None, optional) – Explicit spike timestamp in milliseconds. If None, uses the current simulation time plus one timestep (t + dt). Default: None.

Returns:

Number of spikes actually recorded (rounded multiplicity).

Return type:

int

Raises:
  • ValueError – If multiplicity is not a non-negative integer (after rounding).

  • ValueError – If t_spike_ms is not a scalar or not finite.

Notes

  • Multiple spikes at the same timestamp accumulate exponentially in the Kminus trace: \(K_- \leftarrow (K_- + 1)^{\text{multiplicity}}\).

  • This method does not trigger plasticity updates; it only records spike times for future processing during send() or trigger_update_weight().

Examples

>>> syn = bp.stdp_dopamine_synapse(volume_transmitter=object())
>>> syn.init_state()
>>> # Record single spike at current time
>>> n = syn.record_post_spike(1.0)
>>> print(n)
1
>>> # Record multiple spikes at explicit time
>>> n = syn.record_post_spike(3.0, t_spike_ms=25.0)
>>> print(n)
3
send(multiplicity=1.0, *, post=None, receptor_type=None)[source]#

Schedule one outgoing spike event with dopamine-modulated STDP updates.

Implements the NEST stdp_dopamine_synapse::send method: processes postsynaptic spike history, updates eligibility trace, propagates dopamine and weight dynamics, then schedules a delayed synaptic event to the postsynaptic target.

Parameters:
  • multiplicity (float or array-like, optional) – Presynaptic spike multiplicity (typically 1.0 for single spikes, or higher for burst events). If zero, no event is sent. Default: 1.0.

  • post (Dynamics or None, optional) – Postsynaptic receiver object. If None, uses the synapse’s default post attribute. Default: None.

  • receptor_type (int, array-like, or None, optional) – Target receptor port on postsynaptic neuron. If None, uses the synapse’s default receptor_type. Default: None.

Returns:

True if an event was scheduled, False if multiplicity was zero.

Return type:

bool

Raises:

ValueError – If volume_transmitter is None (must be assigned before sending events).

Notes

Update sequence (following NEST ``stdp_dopamine_synapse::send``):

  1. Retrieve postsynaptic spike history in the window \((t_{\mathrm{last\_update}} - d,\; t_{\mathrm{pre}} - d]\) where \(d\) is dendritic delay.

  2. For each postsynaptic spike \(t_{\mathrm{post}}\) in that window:

    1. Propagate dopamine/eligibility/weight to \(t_{\mathrm{post}} + d\)

    2. If \(t_{\mathrm{pre}} - t_{\mathrm{post}} > \epsilon\), facilitate eligibility trace: \(c \leftarrow c + A_+ K_+(t)\)

  3. Propagate dopamine/eligibility/weight to \(t_{\mathrm{pre}}\)

  4. Depress eligibility trace: \(c \leftarrow c - A_- K_-(t_{\mathrm{pre}} - d)\)

  5. Schedule spike event with updated weight for delivery at \(t_{\mathrm{pre}} + \mathrm{delay}\)

  6. Update presynaptic trace: \(K_+ \leftarrow K_+ \exp((t_{\mathrm{last\_update}} - t_{\mathrm{pre}}) / \tau_+) + 1\)

  7. Set \(t_{\mathrm{last\_update}} = t_{\mathrm{lastspike}} = t_{\mathrm{pre}}\)

The event payload is multiplicity * weight, modulated by dopamine-driven weight changes up to the current spike time.

Timing: Uses current simulation time plus one timestep (t + dt) as the spike timestamp (on-grid timing).

Examples

>>> import brainpy.state as bp
>>> import saiunit as u
>>> # Create postsynaptic neuron and synapse
>>> post_neuron = bp.LIF(1)
>>> syn = bp.stdp_dopamine_synapse(
...     weight=5.0,
...     delay=1.0 * u.ms,
...     post=post_neuron,
...     volume_transmitter=object()
... )
>>> syn.init_state()
>>> # Send presynaptic spike
>>> sent = syn.send(1.0)
>>> print(sent)
True
>>> # Check updated trace
>>> print(f"Kplus after spike: {syn.Kplus:.3f}")
Kplus after spike: 1.000

See also

trigger_update_weight

Propagate state without sending spike event

update

High-level method combining spike delivery, recording, and sending

set(*, weight=<object object>, delay=<object object>, receptor_type=<object object>, volume_transmitter=<object object>, A_plus=<object object>, A_minus=<object object>, tau_plus=<object object>, tau_minus=<object object>, tau_c=<object object>, tau_n=<object object>, b=<object object>, Wmin=<object object>, Wmax=<object object>, Kplus=<object object>, c=<object object>, n=<object object>, post=<object object>)[source]#

Set NEST-style public parameters and mutable state.

Updates synapse parameters and/or state variables. Validates all new values before applying changes. Only parameters explicitly provided are modified.

Parameters:
  • weight (float, array-like, or sentinel, optional) – New synaptic weight. If _UNSET, weight is not changed.

  • delay (Quantity, array-like, or sentinel, optional) – New synaptic delay. If _UNSET, delay is not changed.

  • receptor_type (int or sentinel, optional) – New receptor port identifier. If _UNSET, receptor type is not changed.

  • volume_transmitter (object or sentinel, optional) – New volume transmitter handle. If _UNSET, handle is not changed.

  • A_plus (float, array-like, or sentinel, optional) – New facilitation amplitude. If _UNSET, not changed.

  • A_minus (float, array-like, or sentinel, optional) – New depression amplitude. If _UNSET, not changed.

  • tau_plus (Quantity, array-like, or sentinel, optional) – New presynaptic trace time constant. If _UNSET, not changed.

  • tau_minus (Quantity, array-like, or sentinel, optional) – New postsynaptic trace time constant. If _UNSET, not changed.

  • tau_c (Quantity, array-like, or sentinel, optional) – New eligibility trace time constant. If _UNSET, not changed.

  • tau_n (Quantity, array-like, or sentinel, optional) – New dopamine trace time constant. If _UNSET, not changed.

  • b (float, array-like, or sentinel, optional) – New dopamine baseline. If _UNSET, not changed.

  • Wmin (float, array-like, or sentinel, optional) – New minimum weight bound. If _UNSET, not changed.

  • Wmax (float, array-like, or sentinel, optional) – New maximum weight bound. If _UNSET, not changed.

  • Kplus (float, array-like, or sentinel, optional) – New presynaptic trace value. Must be non-negative. If _UNSET, not changed.

  • c (float, array-like, or sentinel, optional) – New eligibility trace value. If _UNSET, not changed.

  • n (float, array-like, or sentinel, optional) – New dopamine trace value. If _UNSET, not changed.

  • post (object or sentinel, optional) – New default postsynaptic receiver. If _UNSET, not changed.

Raises:

ValueError

  • If any time constant is non-positive. - If Kplus is negative. - If any parameter has non-scalar shape or non-finite value.

Notes

  • Changing parameters during a simulation may produce non-physical discontinuities. For clean state resets, use init_state() instead.

  • Updating initial state values (Kplus, c, n) also updates the stored initial conditions used by init_state().

Examples

>>> syn = bp.stdp_dopamine_synapse(
...     weight=1.0,
...     A_plus=1.0,
...     volume_transmitter=object()
... )
>>> syn.init_state()
>>> # Modify learning rate mid-simulation
>>> syn.set(A_plus=2.0, A_minus=3.0)
>>> print(syn.A_plus)
2.0
>>> # Reset dopamine trace
>>> syn.set(n=0.5)
>>> print(syn.n)
0.5
trigger_update_weight(*, t_trig_ms=None)[source]#

Propagate dopamine, eligibility, and weight to specified trigger time.

Implements NEST stdp_dopamine_synapse::trigger_update_weight: processes postsynaptic spikes, integrates dopamine spike history, updates weight via eligibility trace modulation, and advances all state traces to the trigger time. This is typically called once per simulation timestep to integrate volume transmitter dopamine deliveries.

Parameters:

t_trig_ms (float, array-like, or None, optional) – Target timestamp in milliseconds to which state should be propagated. If None, uses current simulation time plus one timestep (t + dt). Default: None.

Raises:
  • ValueError – If volume_transmitter is None.

  • ValueError – If t_trig_ms is earlier than t_last_update (backward time propagation is not allowed).

  • ValueError – If t_trig_ms is not scalar or not finite.

Notes

Update sequence:

  1. Retrieve postsynaptic spike history in the window \((t_{\mathrm{last\_update}} - d,\; t_{\mathrm{trig}} - d]\) where \(d\) is dendritic delay.

  2. For each postsynaptic spike \(t_{\mathrm{post}}\) in that window:

    1. Propagate dopamine/eligibility/weight to \(t_{\mathrm{post}} + d\)

    2. Facilitate eligibility trace: \(c \leftarrow c + A_+ K_+(t) \exp((t_{\mathrm{last\_update}} - (t_{\mathrm{post}} + d)) / \tau_+)\)

  3. Propagate dopamine/eligibility/weight to \(t_{\mathrm{trig}}\)

  4. Advance dopamine trace: \(n \leftarrow n \exp((t_{\mathrm{last\_dopa}} - t_{\mathrm{trig}}) / \tau_n)\)

  5. Advance presynaptic trace: \(K_+ \leftarrow K_+ \exp((t_{\mathrm{last\_update}} - t_{\mathrm{trig}}) / \tau_+)\)

  6. Set \(t_{\mathrm{last\_update}} = t_{\mathrm{trig}}\)

  7. Reset dopamine spike buffer to \([(t_{\mathrm{trig}}, 0)]\)

Weight integration uses the analytical solution:

\[w \leftarrow w - c(t_0) \left( \frac{n(t_0)}{\tau_s} \mathrm{expm1}(\tau_s \Delta t) - b \tau_c \mathrm{expm1}(\Delta t / \tau_c) \right)\]

where \(\tau_s = (\tau_c + \tau_n) / (\tau_c \tau_n)\) and clipped to \([W_{\min}, W_{\max}]\).

Typical usage: Called once per timestep in update() with trigger_dopa_update=True (default) to synchronize with volume transmitter delivery intervals.

Examples

>>> syn = bp.stdp_dopamine_synapse(
...     weight=5.0,
...     tau_c=1000.0 * u.ms,
...     tau_n=200.0 * u.ms,
...     b=0.1,
...     volume_transmitter=object()
... )
>>> syn.init_state()
>>> # Record some eligibility via spike pairing (not shown)
>>> syn.c = 0.5  # artificial eligibility
>>> # Record dopamine spike
>>> syn.record_dopa_spike(2.0, t_spike_ms=10.0)
>>> # Trigger update to integrate dopamine effect
>>> syn.trigger_update_weight(t_trig_ms=50.0)
>>> print(f"Weight after dopamine: {syn.weight:.3f}")
Weight after dopamine: ...

See also

send

Send presynaptic spike with plasticity updates

record_dopa_spike

Record dopamine delivery event

update

High-level simulation step including trigger_update_weight

update(pre_spike=0.0, *, post_spike=0.0, dopa_spike=0.0, post=None, receptor_type=None, trigger_dopa_update=True)[source]#

Execute one simulation timestep with spike delivery, plasticity, and dopamine updates.

This is the primary high-level interface for advancing synapse dynamics. It integrates spike event delivery, postsynaptic/dopamine spike recording, presynaptic spike processing with STDP updates, and dopamine-modulated weight integration.

Parameters:
  • pre_spike (float or array-like, optional) – Presynaptic spike multiplicity for the current timestep (typically 0.0 or 1.0). Aggregated with any registered input sources via sum_current_inputs() and sum_delta_inputs(). Default: 0.0.

  • post_spike (float or array-like, optional) – Postsynaptic spike multiplicity (integer count, will be rounded). Recorded into internal postsynaptic history for STDP processing. Default: 0.0.

  • dopa_spike (float or array-like, optional) – Dopamine delivery multiplicity (arbitrary non-negative float representing concentration increment). Recorded into internal dopamine history. Default: 0.0.

  • post (Dynamics or None, optional) – Postsynaptic receiver for spike events. If None, uses synapse’s default post attribute. Default: None.

  • receptor_type (int, array-like, or None, optional) – Target receptor port on postsynaptic neuron. If None, uses synapse’s default receptor_type. Default: None.

  • trigger_dopa_update (bool, optional) – If True, calls trigger_update_weight() at the end of the timestep to integrate dopamine effects (standard NEST behavior). If False, skips weight propagation (useful for debugging or custom control). Default: True.

Returns:

Number of delayed synaptic events delivered to postsynaptic targets during this timestep.

Return type:

int

Raises:
  • ValueError – If volume_transmitter is None.

  • ValueError – If post_spike or dopa_spike cannot be converted to valid non-negative scalar counts/multiplicities.

Notes

Update sequence:

  1. Deliver delayed events: Process all events in the delivery queue scheduled for the current simulation step (t). Events are sent to their target neurons.

  2. Record postsynaptic spikes: If post_spike > 0, record spike(s) at timestamp t + dt into internal postsynaptic history. Updates Kminus trace.

  3. Record dopamine spikes: If dopa_spike > 0, record dopamine delivery at timestamp t + dt into internal dopamine history.

  4. Process presynaptic spikes: If pre_spike > 0 (after aggregating input sources), call send() to:

    • Process postsynaptic history for STDP eligibility updates

    • Integrate dopamine/weight dynamics

    • Schedule delayed synaptic event

    • Update presynaptic trace Kplus

  5. Trigger dopamine update (if trigger_dopa_update=True): Call trigger_update_weight() to propagate all traces and weight to t + dt, integrating queued dopamine spikes.

Timing convention: All spikes (pre, post, dopamine) are timestamped at t + dt (on-grid, next simulation step).

Typical usage: Called once per simulation timestep in a training loop.

Examples

Basic simulation loop with pre/post spike pairing:

>>> import brainpy.state as bp
>>> import saiunit as u
>>> import brainstate as bs
>>> # Setup
>>> post_neuron = bp.LIF(1)
>>> syn = bp.stdp_dopamine_synapse(
...     weight=5.0,
...     delay=1.0 * u.ms,
...     post=post_neuron,
...     volume_transmitter=object(),
...     A_plus=0.01,
...     A_minus=0.012
... )
>>> syn.init_state()
>>> post_neuron.init_all_states()
>>> # Simulate causal spike pairing
>>> with bs.environ.context(dt=0.1 * u.ms):
...     delivered = syn.update(pre_spike=1.0, post_spike=0.0)  # pre at t=0.1ms
...     delivered = syn.update(pre_spike=0.0, post_spike=1.0)  # post at t=0.2ms
...     # Deliver reward
...     delivered = syn.update(pre_spike=0.0, post_spike=0.0, dopa_spike=1.0)
>>> print(f"Final weight: {syn.weight:.4f}")
Final weight: ...

Disabling dopamine updates for testing:

>>> syn = bp.stdp_dopamine_synapse(
...     weight=1.0,
...     volume_transmitter=object()
... )
>>> syn.init_state()
>>> # Update without weight propagation
>>> delivered = syn.update(
...     pre_spike=1.0,
...     trigger_dopa_update=False
... )
>>> # Weight remains unchanged (no dopamine integration)
>>> print(syn.weight)
1.0

See also

send

Process presynaptic spike with STDP updates

trigger_update_weight

Propagate state with dopamine integration

record_post_spike

Manually record postsynaptic spike

record_dopa_spike

Manually record dopamine delivery