clopath_synapse#
- class brainpy.state.clopath_synapse(weight=1.0, delay=1.0, delay_steps=1, x_bar=0.0, tau_x=15.0, Wmin=0.0, Wmax=100.0, t_last_spike_ms=0.0, name=None)#
NEST-compatible voltage-based STDP synapse following the Clopath plasticity rule.
This synapse model implements the voltage-based spike-timing-dependent plasticity (STDP) rule described by Clopath et al. (2010). Unlike traditional pair-based STDP, weight updates depend on postsynaptic membrane voltage traces archived by the target neuron, enabling voltage-dependent learning rules that capture homeostatic regulation and triplet interactions.
1. Model Overview
The Clopath synapse is a connection-level model that modulates synaptic weights based on:
Presynaptic spike times and a presynaptic trace \(\bar{x}(t)\)
Postsynaptic voltage-derived traces for long-term potentiation (LTP) and depression (LTD)
Hard bounds \([W_\text{min}, W_\text{max}]\) on synaptic weight
The model requires the postsynaptic neuron to maintain voltage-dependent history buffers (e.g.,
aeif_psc_delta_clopathin NEST).2. State Variables
Each connection maintains:
\(w\) – Current synaptic weight
\(\bar{x}\) – Presynaptic trace (low-pass filtered spike train)
\(t_\text{last}\) – Timestamp of most recent presynaptic spike (milliseconds)
\(\tau_x\) – Time constant for presynaptic trace decay (milliseconds)
\(W_\text{min}, W_\text{max}\) – Hard lower/upper weight bounds
3. Plasticity Update Sequence
On each presynaptic spike at time \(t\) (with dendritic delay \(d\) and previous spike time \(t_\text{last}\)), the following steps are executed in order:
(a) Long-Term Potentiation (LTP):
Retrieve all LTP history entries from the postsynaptic neuron in the interval \((t_\text{last} - d,\, t - d]\). For each entry with timestamp \(t_i\) and amplitude \(\text{dw}_i\):
\[w \leftarrow \min\left(W_\text{max},\, w + \text{dw}_i \cdot \bar{x} \exp\left(\frac{t_\text{last} - (t_i + d)}{\tau_x}\right)\right)\]This facilitates the weight proportional to the presynaptic trace at the effective time \(t_i + d\), accounting for exponential decay from \(t_\text{last}\).
(b) Long-Term Depression (LTD):
Query the postsynaptic LTD value at the effective time \(t - d\):
\[w \leftarrow \max(W_\text{min},\, w - \text{dw}_\text{LTD}(t - d))\]This depresses the weight by the current LTD trace magnitude, clamped to \(W_\text{min}\).
(c) Event Emission:
A spike event is generated with the updated weight and delay parameters.
(d) Presynaptic Trace Update:
The presynaptic trace is updated to account for the new spike:
\[\bar{x} \leftarrow \bar{x} \exp\left(\frac{t_\text{last} - t}{\tau_x}\right) + \frac{1}{\tau_x}\](e) Timestamp Update:
The last spike time is updated: \(t_\text{last} \leftarrow t\).
4. Mathematical Foundations
The presynaptic trace \(\bar{x}(t)\) is a low-pass filter of the presynaptic spike train \(S_\text{pre}(t) = \sum_k \delta(t - t_k)\):
\[\tau_x \frac{d\bar{x}}{dt} = -\bar{x} + S_\text{pre}(t)\]At each spike time \(t_k\), the exact solution yields the jump condition:
\[\bar{x}(t_k^+) = \bar{x}(t_k^-) e^{-(t_k - t_{k-1})/\tau_x} + \frac{1}{\tau_x}\]This exact event-driven update is implemented in step (d).
5. Postsynaptic Interface Requirements
The target neuron must provide:
get_LTP_history(t1, t2)orget_ltp_history(t1, t2): Returns iterable of LTP events in \((t_1, t_2]\)get_LTD_value(t)orget_ltd_value(t): Returns scalar LTD amplitude at time \(t\)
Each LTP history entry must support extraction of:
Time field:
t_,t,time_ms, ortimeWeight change field:
dw_,dw,delta_w, orweight_change
Supported entry formats:
Object with attributes
t_anddw_Object with attributes
tanddwDictionary with keys
't'/'t_'and'dw'/'dw_'2-tuple
(t, dw)
- Parameters:
weight (
float, optional) – Initial synaptic weight (dimensionless). Must satisfy sign consistency withWminandWmax. Default:1.0.delay (
float, optional) – Dendritic propagation delay in milliseconds. Must be positive. Default:1.0.delay_steps (
int, optional) – Integer delay in simulation time steps for event delivery. Must be >= 1. Default:1.x_bar (
float, optional) – Initial presynaptic trace value (dimensionless). Typically initialized to0.0before any spikes. Default:0.0.tau_x (
float, optional) – Time constant for presynaptic trace exponential decay (milliseconds). Must be positive and non-zero. Controls the temporal window of LTP. Typical values: 10-20 ms. Default:15.0.Wmin (
float, optional) – Hard lower bound on synaptic weight (dimensionless). Must have same sign asweightaccording to NEST’s internal sign checks. Default:0.0.Wmax (
float, optional) – Hard upper bound on synaptic weight (dimensionless). Must have same sign asweightaccording to NEST’s internal sign checks. Default:100.0.t_last_spike_ms (
float, optional) – Timestamp of the most recent presynaptic spike (milliseconds). Initialized to0.0before the first spike. Default:0.0.name (
strorNone, optional) – Optional identifier for this connection instance. Default:None.
Parameter Mapping
This table shows the correspondence between brainpy.state and NEST parameter names:
brainpy.state
NEST
Unit
Description
weightweight(unitless)
Synaptic weight
delaydelayms
Dendritic delay
delay_steps(runtime)
steps
Event delivery delay
x_barx_bar(unitless)
Presynaptic trace
tau_xtau_xms
Presynaptic time constant
WminWmin(unitless)
Minimum weight
WmaxWmax(unitless)
Maximum weight
t_last_spike_ms(internal state)
ms
Last spike timestamp
- REQUIRES_CLOPATH_ARCHIVING#
Connection requires voltage trace archiving from postsynaptic neuron. Always
True.- Type:
- Raises:
ValueError – If
weight,Wmin, andWmaxdo not satisfy sign consistency constraints. NEST enforces:sign(weight) == sign(Wmin)andsign(weight) == sign(Wmax), where sign tests use different comparison operators for min vs max bounds.ValueError – If
delay<= 0 ordelay_steps< 1.ValueError – If any parameter is non-finite (NaN or Inf).
ValueError – If
tau_xis zero (division by zero in trace updates).AttributeError – If target neuron does not provide required
get_LTP_historyandget_LTD_valuemethods duringsend()call.
See also
aeif_psc_delta_clopathAdaptive exponential IF neuron with Clopath archiving (NEST)
hh_psc_alpha_clopathHodgkin-Huxley neuron with Clopath archiving (NEST)
stdp_synapseTraditional pair-based STDP synapse
Notes
Implementation Details:
All internal computations use 64-bit floating point (
float64) to match NEST precision.Precise sub-grid spike timing offsets are ignored; all spike times are treated as exact multiples of the simulation time step.
The update sequence strictly follows
clopath_synapse::send()in NEST to ensure numerical equivalence.Sign constraints use NEST’s asymmetric comparison operators:
Wminuses>=vs<whileWmaxuses>vs<=.
Biological Interpretation:
The Clopath rule captures key experimental observations:
LTP depends on presynaptic activity (spike trace \(\bar{x}\)) and postsynaptic depolarization (voltage-derived LTP trace).
LTD depends on presynaptic spikes paired with postsynaptic voltage without strong depolarization.
The voltage dependence enables homeostatic regulation: neurons with high baseline firing rates have reduced LTP, preventing runaway excitation.
The model reproduces triplet STDP effects without explicit triplet terms.
Computational Considerations:
Memory overhead scales with the number of LTP history entries archived by the postsynaptic neuron (typically bounded by a sliding time window).
For large fan-in networks, the LTP history query in step (a) may become a bottleneck. Consider using sparse indexing or binned histograms for postsynaptic traces.
The exponential decay calculations use
math.expfor scalar operations. For vectorized implementations, replace withjax.numpy.expor equivalent.
References
Examples
Basic Usage:
Create a Clopath synapse with default parameters:
>>> import brainpy.state as bst >>> synapse = bst.clopath_synapse(weight=0.5, tau_x=15.0, Wmin=0.0, Wmax=1.0) >>> synapse.get_status() {'weight': 0.5, 'tau_x': 15.0, 'Wmin': 0.0, 'Wmax': 1.0, ...}
Simulating Presynaptic Spike Train:
Assuming a postsynaptic neuron with Clopath archiving:
>>> # Mock target neuron with required interface >>> class ClopathNeuron: ... def get_ltp_history(self, t1, t2): ... # Return LTP events in (t1, t2] ... return [(10.5, 0.05), (12.3, 0.08)] # (time_ms, dw) ... def get_ltd_value(self, t): ... # Return LTD amplitude at time t ... return 0.02 >>> target = ClopathNeuron() >>> synapse = bst.clopath_synapse(weight=1.0, tau_x=10.0, Wmin=0.0, Wmax=5.0) >>> # Process spike train >>> spike_times = [10.0, 20.0, 30.0] >>> events = synapse.simulate_pre_spike_train(spike_times, target) >>> print(f"Final weight: {synapse.weight:.3f}") Final weight: 1.123
Weight Evolution with Voltage-Dependent Plasticity:
>>> synapse = bst.clopath_synapse(weight=1.0, tau_x=15.0, Wmin=-2.0, Wmax=2.0) >>> # Simulate pairing protocol: pre before post (LTP) >>> for t in [10, 20, 30]: ... event = synapse.send(t_spike_ms=t, target=target) >>> print(f"Weight after LTP protocol: {synapse.weight:.3f}") Weight after LTP protocol: 1.450
Sign Constraint Validation:
>>> # Valid: all same sign (positive) >>> synapse = bst.clopath_synapse(weight=1.0, Wmin=0.0, Wmax=5.0) >>> # Invalid: mixed signs >>> try: ... synapse = bst.clopath_synapse(weight=1.0, Wmin=-1.0, Wmax=5.0) ... except ValueError as e: ... print(e) Weight and Wmin must have same sign.
- get(key='status')[source]#
Retrieve connection status or specific parameter value.
- Parameters:
key (
str, optional) – Key to retrieve. Use'status'for full status dictionary, or specify a parameter name (e.g.,'weight','tau_x','Wmin'). Default:'status'.- Returns:
If
key == 'status', returns full status dictionary. Otherwise returns the requested parameter value with type matching the parameter (float, int, or bool).- Return type:
- Raises:
KeyError – If
keyis not'status'and does not match any parameter or property name in the status dictionary.
Examples
>>> synapse = bst.clopath_synapse(weight=1.5, tau_x=12.0) >>> synapse.get('weight') 1.5 >>> synapse.get('tau_x') 12.0 >>> status = synapse.get('status') >>> status['Wmax'] 100.0
- get_status()[source]#
Retrieve current connection state and parameter values.
- Returns:
Dictionary containing all connection parameters, state variables, and properties:
'weight'(float): Current synaptic weight'delay'(float): Dendritic delay in milliseconds'delay_steps'(int): Integer delay in simulation steps'x_bar'(float): Current presynaptic trace value'tau_x'(float): Presynaptic trace time constant (ms)'Wmin'(float): Minimum weight bound'Wmax'(float): Maximum weight bound't_last_spike_ms'(float): Last presynaptic spike time (ms)'size_of'(int): Memory footprint in bytes'has_delay'(bool): Delay support flag'is_primary'(bool): Primary connection flag'requires_clopath_archiving'(bool): Archiving requirement flag'supports_hpc'(bool): HPC support flag'supports_lbl'(bool): Label-based lookup flag'supports_wfr'(bool): Waveform relaxation flag
- Return type:
dict[str,Any]
Notes
This method provides NEST-compatible status retrieval. All values are returned as Python native types (float, int, bool) rather than NumPy arrays.
- property properties: dict[str, Any]#
Return dictionary of connection model properties and capabilities.
- Returns:
Dictionary with keys:
'has_delay'(bool): Connection supports propagation delay'is_primary'(bool): Connection is primary type'requires_clopath_archiving'(bool): Requires voltage trace archiving'supports_hpc'(bool): High-performance computing support'supports_lbl'(bool): Label-based lookup support'supports_wfr'(bool): Waveform relaxation support
- Return type:
dict[str,Any]
- send(t_spike_ms, target, receptor_type=0, multiplicity=1.0, delay=None, delay_steps=None)[source]#
Process one presynaptic spike with Clopath plasticity and return spike event payload.
This method implements the core plasticity update sequence:
Query postsynaptic LTP history in the interval since last presynaptic spike
Apply facilitation (LTP) for each history entry using decayed presynaptic trace
Apply depression (LTD) at the current effective spike time
Update presynaptic trace and last spike timestamp
Return spike event with updated weight
- Parameters:
t_spike_ms (
floatorarray-like) – Current presynaptic spike time in milliseconds (scalar). Must be finite.target (
object) – Postsynaptic neuron or target object. Must provideget_LTP_history(t1, t2)(orget_ltp_history) andget_LTD_value(t)(orget_ltd_value) methods.receptor_type (
intorarray-like, optional) – Receptor port index on target neuron (scalar integer). Default:0.multiplicity (
floatorarray-like, optional) – Spike event multiplicity (scalar, >= 0). Used for batch spike processing. Default:1.0.delay (
floatorarray-likeorNone, optional) – Override dendritic delay for this spike (milliseconds, scalar). IfNone, uses connection’s storedself.delay. Default:None.delay_steps (
intorarray-likeorNone, optional) – Override integer delay in time steps for this event (scalar). IfNone, uses connection’s storedself.delay_steps. Default:None.
- Returns:
Spike event payload dictionary with keys:
'weight'(float): Updated synaptic weight after plasticity'delay'(float): Dendritic delay used (milliseconds)'delay_steps'(int): Integer delay in time steps'receptor_type'(int): Target receptor port index'multiplicity'(float): Spike multiplicity't_spike_ms'(float): Presynaptic spike timestamp
- Return type:
dict[str,Any]- Raises:
ValueError – If
tau_xis zero (division by zero in exponential decay calculations).ValueError – If any parameter is non-scalar or non-finite.
ValueError – If
delay<= 0,delay_steps< 1, ormultiplicity< 0.AttributeError – If
targetdoes not provide requiredget_LTP_historyandget_LTD_valuemethods (or their lowercase variants).ValueError – If any LTP history entry does not provide extractable time and weight change fields.
Notes
Update Sequence Details:
Let \(t\) =
t_spike_ms, \(d\) = effective delay, \(t_\text{last}\) =self.t_last_spike_ms.Step 1: LTP Application
For each LTP history entry \((t_i, \text{dw}_i)\) in \((t_\text{last} - d, t - d]\):
\[w \leftarrow \min(W_\text{max},\, w + \text{dw}_i \cdot \bar{x} \exp((t_\text{last} - (t_i + d)) / \tau_x))\]Step 2: LTD Application
\[w \leftarrow \max(W_\text{min},\, w - \text{dw}_\text{LTD}(t - d))\]Step 3: Presynaptic Trace Update
\[\bar{x} \leftarrow \bar{x} \exp((t_\text{last} - t) / \tau_x) + 1 / \tau_x\]Step 4: Timestamp Update
\[t_\text{last} \leftarrow t\]Side Effects
This method modifies connection state:
self.weight: Updated by LTP and LTDself.x_bar: Updated with new spike contributionself.t_last_spike_ms: Set to current spike time
Performance Considerations:
The LTP history query dominates runtime for neurons with many incoming connections. Consider using bounded history buffers (sliding window) in the target neuron to limit the number of entries returned.
Examples
Process single spike:
>>> class MockTarget: ... def get_ltp_history(self, t1, t2): ... return [(5.5, 0.1)] # One LTP event ... def get_ltd_value(self, t): ... return 0.02 >>> target = MockTarget() >>> synapse = bst.clopath_synapse(weight=1.0, tau_x=10.0, Wmin=0.0, Wmax=5.0) >>> event = synapse.send(t_spike_ms=10.0, target=target) >>> print(f"Updated weight: {event['weight']:.3f}") Updated weight: 1.023
Process spike with custom delay:
>>> event = synapse.send(t_spike_ms=20.0, target=target, delay=2.5) >>> print(f"Delay used: {event['delay']} ms") Delay used: 2.5 ms
- set_delay(delay)[source]#
Set dendritic propagation delay.
- Parameters:
delay (
floatorarray-like) – New delay in milliseconds (scalar). Must be positive.- Raises:
ValueError – If
delayis non-scalar, non-finite, or <= 0.
- set_delay_steps(delay_steps)[source]#
Set integer delay in simulation time steps.
- Parameters:
delay_steps (
intorarray-like) – New delay in time steps (scalar integer). Must be >= 1.- Raises:
ValueError – If
delay_stepsis non-scalar, non-finite, non-integer, or < 1.
- set_status(status=None, **kwargs)[source]#
Update connection parameters and state variables.
- Parameters:
status (
dict[str,Any]orNone, optional) – Dictionary of parameter name-value pairs to update. Supported keys:'weight','delay','delay_steps','x_bar','tau_x','Wmin','Wmax','t_last_spike_ms'. Default:None.**kwargs – Additional parameter updates as keyword arguments. These are merged with
statusdictionary; keyword arguments take precedence.
- Raises:
ValueError – If updated parameters violate sign consistency constraints (
weight,Wmin,Wmaxmust all have compatible signs).ValueError – If
delay<= 0 ordelay_steps< 1.ValueError – If any parameter value is non-finite (NaN or Inf).
Notes
This method provides NEST-compatible parameter setting. Sign constraints are re-checked after all updates are applied. If multiple parameters are updated together, validation occurs atomically after all changes.
Examples
Update single parameter:
>>> synapse = bst.clopath_synapse(weight=1.0) >>> synapse.set_status(weight=0.5) >>> synapse.get_status()['weight'] 0.5
Update multiple parameters:
>>> synapse.set_status({'weight': 2.0, 'tau_x': 20.0}) >>> synapse.set_status(Wmin=0.0, Wmax=5.0)
- set_weight(weight)[source]#
Set synaptic weight value.
- Parameters:
weight (
floatorarray-like) – New synaptic weight value (scalar). Must be finite and satisfy sign consistency withWminandWmax.- Raises:
ValueError – If
weightis non-scalar, non-finite, or violates sign constraints.
Notes
This is a convenience method equivalent to
set_status(weight=...), but does not re-check sign constraints (assumes they were satisfied during initialization).
- simulate_pre_spike_train(spike_times_ms, target, receptor_type=0, multiplicity=1.0, delay=None, delay_steps=None)[source]#
Process a sequence of presynaptic spikes and return event payloads for each.
This method sequentially processes multiple presynaptic spikes, updating connection state (weight, presynaptic trace, last spike time) after each spike. The plasticity updates are cumulative: each spike’s LTP/LTD application affects the weight seen by subsequent spikes.
- Parameters:
spike_times_ms (
array-like) – Array of presynaptic spike times in milliseconds. Values are converted to 1-D float64 array and processed in order. Must contain finite values.target (
object) – Postsynaptic neuron providingget_LTP_historyandget_LTD_valuemethods.receptor_type (
intorarray-like, optional) – Receptor port index (scalar). Default:0.multiplicity (
floatorarray-like, optional) – Spike multiplicity for all events (scalar). Default:1.0.delay (
floatorarray-likeorNone, optional) – Dendritic delay override (milliseconds). IfNone, usesself.delay. Default:None.delay_steps (
intorarray-likeorNone, optional) – Integer delay override (time steps). IfNone, usesself.delay_steps. Default:None.
- Returns:
List of spike event payloads, one per input spike time. Each event dictionary contains the same keys as returned by
send():'weight','delay','delay_steps','receptor_type','multiplicity','t_spike_ms'.- Return type:
list[dict[str,Any]]- Raises:
ValueError – If any spike time is non-finite or if
tau_xis zero.AttributeError – If
targetdoes not provide required methods.
Notes
Ordering Effects:
Spike times are processed in array order (not necessarily sorted by time). For biologically realistic simulations, ensure
spike_times_msis sorted in ascending order. Out-of-order spikes may produce unphysical weight trajectories due to incorrect exponential decay calculations.State Evolution:
After processing spike train
[t1, t2, ..., tn], the connection state reflects:self.weight: Cumulative effect of all LTP/LTD updatesself.x_bar: Presynaptic trace at timetnself.t_last_spike_ms: Set totn
Memory Considerations:
The returned event list stores a separate dictionary for each spike. For very long spike trains (>10^6 spikes), consider processing in batches to reduce memory overhead.
Examples
Process spike train and track weight evolution:
>>> class MockTarget: ... def get_ltp_history(self, t1, t2): ... # Return one LTP event per query interval ... if t2 > t1: ... return [((t1 + t2) / 2, 0.05)] ... return [] ... def get_ltd_value(self, t): ... return 0.01 >>> target = MockTarget() >>> synapse = bst.clopath_synapse(weight=1.0, tau_x=10.0, Wmin=0.0, Wmax=3.0) >>> spike_times = [10.0, 20.0, 30.0, 40.0, 50.0] >>> events = synapse.simulate_pre_spike_train(spike_times, target) >>> weights = [evt['weight'] for evt in events] >>> print(f"Weight trajectory: {weights}") Weight trajectory: [1.045, 1.083, 1.115, 1.142, 1.165]
Verify presynaptic trace evolution:
>>> synapse = bst.clopath_synapse(weight=1.0, tau_x=15.0) >>> events = synapse.simulate_pre_spike_train([0.0, 15.0, 30.0], target) >>> print(f"Final x_bar: {synapse.x_bar:.3f}") Final x_bar: 0.091
- to_spike_event(t_spike_ms, target, receptor_type=0, multiplicity=1.0, delay=None, delay_steps=None)[source]#
Alias for
send()method with identical semantics.This method provides an alternative name for spike event generation, maintaining compatibility with different naming conventions. All parameters and return values are identical to
send().See also
sendPrimary spike processing method with full documentation