urbanczik_synapse#
- class brainpy.state.urbanczik_synapse(weight=1.0, delay=1.0, delay_steps=1, tau_Delta=100.0, eta=0.07, Wmin=0.0, Wmax=100.0, PI_integral=0.0, PI_exp_integral=0.0, tau_L_trace=0.0, tau_s_trace=0.0, t_last_spike_ms=-1.0, name=None)#
NEST-compatible
urbanczik_synapseconnection model.Plastic synapse implementing Urbanczik-Senn dendritic prediction error learning rule for supervised learning in multi-compartment neurons. This synapse requires target neurons that archive dendritic prediction errors (e.g.,
pp_cond_exp_mc_urbanczik).1. Mathematical Model
This implementation reproduces the connection-level semantics of NEST
models/urbanczik_synapse.{h,cpp}. The learning rule combines presynaptic spike traces with postsynaptic dendritic prediction errors to update synaptic weights through a low-pass filtered plasticity signal.1.1. Presynaptic Traces
Two exponential traces track presynaptic spiking activity with different time constants:
\[\tau_L^\mathrm{tr}(t) = \tau_L^\mathrm{tr}(t_{last}) \exp\left(\frac{t_{last}-t}{\tau_L}\right) + 1\]\[\tau_s^\mathrm{tr}(t) = \tau_s^\mathrm{tr}(t_{last}) \exp\left(\frac{t_{last}-t}{\tau_s}\right) + 1\]where \(t\) is current spike time, \(t_{last}\) is previous spike time, \(\tau_L = C_m / g_L\) is membrane time constant, and \(\tau_s\) is synaptic time constant (
tau_syn_exfor excitatory weights,tau_syn_infor inhibitory).1.2. Plasticity Signal
For each postsynaptic dendritic prediction error entry \((t_i, \Delta w_i)\) in the history window \((t_{last} - d, t - d]\) (where \(d\) is dendritic delay):
\[\Pi_i = \left[\tau_L^\mathrm{tr}\exp\left(\frac{t_{last}-(t_i+d)}{\tau_L}\right) - \tau_s^\mathrm{tr}\exp\left(\frac{t_{last}-(t_i+d)}{\tau_s}\right)\right] \Delta w_i\]Two integrals accumulate plasticity contributions:
\[\Pi_\mathrm{int} \leftarrow \Pi_\mathrm{int} + \sum_i \Pi_i\]\[\Pi_\mathrm{exp} \leftarrow \exp\left(\frac{t_{last}-t}{\tau_\Delta}\right)\Pi_\mathrm{exp} + \sum_i \exp\left(\frac{(t_i+d)-t}{\tau_\Delta}\right)\Pi_i\]where \(\tau_\Delta\) is the low-pass filter time constant for weight changes.
1.3. Weight Update
The synaptic weight is updated using the filtered difference of integrals:
\[w \leftarrow \mathrm{clip}\left(w_0 + \frac{15\,C_m\,\tau_s\,\eta}{g_L(\tau_L-\tau_s)} (\Pi_\mathrm{int} - \Pi_\mathrm{exp}), W_{min}, W_{max}\right)\]where \(w_0\) is
init_weight, \(\eta\) is learning rate, and \(C_m\), \(g_L\) are membrane capacitance and leak conductance of the dendritic compartment.2. NEST Implementation Fidelity
This class preserves NEST’s exact send-ordering in
urbanczik_synapse::send(...):Read archived history in \((t_{last} - d, t - d]\)
Update \(\Pi_\mathrm{int}\) and \(\Pi_\mathrm{exp}\) integrals
Compute new weight with clipping
Emit spike event with updated weight
Update \(\tau_L^\mathrm{tr}\) and \(\tau_s^\mathrm{tr}\) traces
Set \(t_{last} = t\)
3. Computational Considerations
Synaptic time constant selection: Uses
tau_syn_exwhenweight > 0, otherwisetau_syn_in, matching NEST’s current-weight-dependent branchingNumerical precision: Sub-grid timestamp offsets are ignored as in NEST
Weight bounds: Hard clipping to [Wmin, Wmax] after each update
Sign constraints: Weight, Wmin, and Wmax must all share the same sign (enforced at init and status updates)
4. Target Neuron Requirements
The target neuron must implement the Urbanczik archiving interface:
get_urbanczik_history(t1, t2, comp): Returns prediction error entries in \((t1, t2]\) for compartmentcomp(default 1 for dendritic)get_g_L(comp),get_tau_L(comp)orget_C_m(comp)/get_g_L(comp)get_C_m(comp),get_tau_syn_ex(comp),get_tau_syn_in(comp)
History entries support multiple formats: objects with
t_/dw_attributes (NEST-style), objects witht/dwattributes, dicts with those keys, or 2-tuples(t, dw).- Parameters:
weight (
floatorArrayLike, optional) – Initial synaptic weight (dimensionless). Must share sign with Wmin and Wmax. Default:1.0delay (
floatorArrayLike, optional) – Dendritic delay in milliseconds used for history lookup. Must be> 0. Default:1.0msdelay_steps (
intorArrayLike, optional) – Event delivery delay in simulation time steps. Must be>= 1. Default:1tau_Delta (
floatorArrayLike, optional) – Time constant in milliseconds for low-pass filtering of weight changes. Controls the temporal smoothing of plasticity signals. Larger values produce slower, more stable learning. Default:100.0mseta (
floatorArrayLike, optional) – Learning rate (dimensionless). Scales the magnitude of weight updates. Typical range 0.01–0.1 for cortical models. Default:0.07Wmin (
floatorArrayLike, optional) – Lower bound of synaptic weight (hard clipping). Must share sign with weight and Wmax. Default:0.0Wmax (
floatorArrayLike, optional) – Upper bound of synaptic weight (hard clipping). Must share sign with weight and Wmin. Default:100.0PI_integral (
floatorArrayLike, optional) – Initial value of unfiltered accumulated plasticity integral \(\Pi_\mathrm{int}\). Default:0.0PI_exp_integral (
floatorArrayLike, optional) – Initial value of exponentially filtered plasticity integral \(\Pi_\mathrm{exp}\). Default:0.0tau_L_trace (
floatorArrayLike, optional) – Initial state of \(\tau_L\) presynaptic trace. Default:0.0tau_s_trace (
floatorArrayLike, optional) – Initial state of \(\tau_s\) presynaptic trace. Default:0.0t_last_spike_ms (
floatorArrayLike, optional) – Last presynaptic spike time in milliseconds. Default:-1.0(no previous spike)name (
str, optional) – Instance name for debugging and logging. Default:None
Parameter Mapping
NEST parameter mappings to this implementation:
NEST Parameter
brainpy.state Attribute
weightweight(current synaptic weight)delaydelay(dendritic delay for history)tau_Deltatau_Delta(low-pass time constant)etaeta(learning rate)WminWmin(lower weight bound)WmaxWmax(upper weight bound)init_weightinit_weight(baseline weight for updates)receptor_typepassed per spike event
t_lastspiket_last_spike_ms- Raises:
ValueError – If
delayis not positive,delay_steps < 1, or weight/Wmin/Wmax have inconsistent signsAttributeError – If target neuron does not implement required Urbanczik archiving interface methods
Notes
set_status() behavior: Following NEST,
set_status()always resetsinit_weightto the currentweightunless explicitly provided in the status dictMultiplicity: Spike multiplicity is validated but not used in plasticity computation (NEST compatibility)
Sub-grid timing: Precise spike time offsets within a time step are ignored in this plasticity rule (consistent with NEST implementation)
Examples
Basic synapse creation and spike processing:
>>> import brainpy.state as bp >>> # Create synapse with moderate learning rate >>> syn = bp.urbanczik_synapse( ... weight=0.5, ... delay=1.0, ... tau_Delta=80.0, ... eta=0.05, ... Wmin=0.0, ... Wmax=10.0 ... ) >>> >>> # Check initial status >>> status = syn.get_status() >>> print(f"Initial weight: {status['weight']}") Initial weight: 0.5 >>> print(f"Learning rate: {status['eta']}") Learning rate: 0.05
Processing spike trains with mock target neuron:
>>> class MockUrbanczikNeuron: ... def __init__(self): ... self.history = [] ... ... def get_urbanczik_history(self, t1, t2, comp): ... # Return prediction errors in (t1, t2] ... return [(t, dw) for t, dw in self.history if t1 < t <= t2] ... ... def get_g_L(self, comp): return 10.0 # nS ... def get_tau_L(self, comp): return 20.0 # ms ... def get_C_m(self, comp): return 200.0 # pF ... def get_tau_syn_ex(self, comp): return 2.0 # ms ... def get_tau_syn_in(self, comp): return 5.0 # ms >>> >>> target = MockUrbanczikNeuron() >>> >>> # Simulate presynaptic spike at t=10 ms >>> event = syn.send(t_spike_ms=10.0, target=target) >>> print(f"Weight after spike: {event['weight']:.3f}") Weight after spike: 0.500 >>> >>> # Add dendritic prediction error and process another spike >>> target.history.append((8.0, 0.1)) # (time_ms, delta_w) >>> event = syn.send(t_spike_ms=20.0, target=target) >>> print(f"Weight after learning: {event['weight']:.3f}") Weight after learning: 0.503
Weight bound enforcement:
>>> syn = bp.urbanczik_synapse(weight=5.0, Wmin=0.0, Wmax=10.0) >>> syn.set_status({'weight': 12.0}) # Exceeds Wmax >>> print(syn.get('weight')) 10.0 >>> syn.set_status({'weight': -1.0}) # Violates sign constraint Traceback (most recent call last): ValueError: Weight and Wmax must have same sign.
References
See also
pp_cond_exp_mc_urbanczikMulti-compartment neuron supporting Urbanczik archiving
stdp_synapseClassical spike-timing dependent plasticity synapse
- get(key='status')[source]#
Retrieve synapse parameter or full status.
- Parameters:
key (
str, optional) – Parameter name or'status'for complete state dict. Valid keys match those inget_status()return dict. Default:'status'- Returns:
If
key='status', returns full status dict. Otherwise returns scalar value of requested parameter.- Return type:
Any- Raises:
KeyError – If
keyis not a recognized parameter name.
Examples
>>> syn = bp.urbanczik_synapse(weight=1.5, eta=0.06) >>> syn.get('weight') 1.5 >>> syn.get('eta') 0.06 >>> full_status = syn.get('status') >>> print(type(full_status)) <class 'dict'>
- get_status()[source]#
Return current synapse state and parameters.
- Returns:
Complete synapse state dictionary. Keys include:
weight(float),delay(float),delay_steps(int),tau_Delta(float),eta(float),Wmin(float),Wmax(float),init_weight(float),PI_integral(float),PI_exp_integral(float),tau_L_trace(float),tau_s_trace(float),t_last_spike_ms(float),size_of(int), and capability flags (has_delay,is_primary, etc.).- Return type:
dict[str,Any]
Notes
Compatible with NEST
GetStatus()semantics. All floating-point values are guaranteed finite (no NaN or infinity).Examples
>>> syn = bp.urbanczik_synapse(weight=2.5, eta=0.08) >>> status = syn.get_status() >>> print(f"Weight: {status['weight']}, Learning rate: {status['eta']}") Weight: 2.5, Learning rate: 0.08
- property properties: dict[str, Any]#
Return synapse capability flags.
- Returns:
Dictionary with boolean flags: -
has_delay: Connection supports transmission delays (always True) -is_primary: This is a primary connection type (always True) -requires_urbanczik_archiving: Target must implement Urbanczik history (always True) -supports_hpc: Compatible with high-performance computing features (always True) -supports_lbl: Supports local branching levels in dendritic trees (always True) -supports_wfr: Supports waveform relaxation methods (always True)- Return type:
dict[str,Any]
Notes
These flags match NEST synapse property conventions for integration with NEST-compatible simulation infrastructure.
- send(t_spike_ms, target, receptor_type=0, multiplicity=1.0, delay=None, delay_steps=None)[source]#
Process presynaptic spike, update weight via dendritic prediction errors, and emit event.
This method implements the core Urbanczik-Senn plasticity computation. It retrieves postsynaptic prediction error history from the target neuron, updates presynaptic traces and plasticity integrals, computes new synaptic weight, and returns spike event payload.
Computation Order (NEST-exact):
Query target’s
get_urbanczik_history()for entries in \((t_{last} - d, t - d]\)For each history entry, compute \(\Pi_i\) using current trace states
Update \(\Pi_\mathrm{int}\) and \(\Pi_\mathrm{exp}\) accumulators
Compute new weight with clipping to [Wmin, Wmax]
Create spike event dict with updated weight
Update \(\tau_L^\mathrm{tr}\) and \(\tau_s^\mathrm{tr}\) traces
Set \(t_{last} = t\)
- Parameters:
t_spike_ms (
floatorArrayLike) – Presynaptic spike time in milliseconds. Must be finite scalar.target (
Any) – Postsynaptic neuron implementing Urbanczik archiving interface. Must provide:get_urbanczik_history(t1, t2, comp),get_g_L(comp),get_tau_L(comp)(orget_C_m(comp)),get_tau_syn_ex(comp),get_tau_syn_in(comp).receptor_type (
intorArrayLike, optional) – Receptor channel index on target neuron. Default:0multiplicity (
floatorArrayLike, optional) – Spike event multiplicity (validated but not used in plasticity). Must be>= 0. Default:1.0delay (
floatorArrayLike, optional) – Override dendritic delay for this spike (milliseconds, must be> 0). IfNone, usesself.delay. Default:Nonedelay_steps (
intorArrayLike, optional) – Override event delivery delay for this spike (steps, must be>= 1). IfNone, usesself.delay_steps. Default:None
- Returns:
Spike event dictionary. Keys:
weight(float, updated synaptic weight),delay(float, dendritic delay in ms),delay_steps(int),receptor_type(int),multiplicity(float),t_spike_ms(float, spike time in ms),tau_s_ms(float, synaptic time constant used),PI_integral(float),PI_exp_integral(float),tau_L_trace_post(float),tau_s_trace_post(float).- Return type:
dict[str,Any]- Raises:
AttributeError – If target does not implement required Urbanczik archiving interface methods.
ValueError – If any parameter is non-finite, delay is not positive, delay_steps < 1, or multiplicity < 0.
Notes
Synaptic time constant selection: Uses
tau_syn_exif currentweight > 0, otherwisetau_syn_in. This branching matches NEST’s current-weight-dependent logic.Sub-grid precision: Sub-timestep spike offsets are ignored in plasticity computation (NEST compatibility).
History window: Query interval \((t_{last} - d, t - d]\) is open on left, closed on right, matching NEST’s
get_history()semantics.Multiplicity: Validated but not incorporated into weight update (NEST behavior).
Examples
Process single spike:
>>> syn = bp.urbanczik_synapse(weight=1.0, eta=0.05) >>> event = syn.send(t_spike_ms=10.0, target=mock_neuron) >>> print(f"Updated weight: {event['weight']:.3f}") Updated weight: 1.003
Override delay for specific spike:
>>> event = syn.send(t_spike_ms=20.0, target=mock_neuron, delay=2.0) >>> print(f"Delay used: {event['delay']} ms") Delay used: 2.0 ms
Access trace states post-update:
>>> event = syn.send(t_spike_ms=30.0, target=mock_neuron) >>> print(f"tau_L trace: {event['tau_L_trace_post']:.3f}") tau_L trace: 1.105
- set_delay(delay)[source]#
Update dendritic delay.
- Parameters:
delay (
floatorArrayLike) – New dendritic delay in milliseconds. Must be> 0.- Raises:
ValueError – If delay is not positive, non-finite, or non-scalar.
- set_delay_steps(delay_steps)[source]#
Update event delivery delay in time steps.
- Parameters:
delay_steps (
intorArrayLike) – New event delivery delay. Must be>= 1.- Raises:
ValueError – If delay_steps is less than 1 or not an integer value.
- set_status(status=None, **kwargs)[source]#
Update synapse parameters and state.
- Parameters:
status (
dict[str,Any], optional) – Dictionary of parameter updates. Keys match those returned byget_status().**kwargs – Additional parameter updates as keyword arguments. These override any values in
statusdict if both are provided.
- Raises:
ValueError – If updated parameters violate sign constraints (weight, Wmin, Wmax must share sign), if delay is not positive, if delay_steps < 1, or if any value is non-finite.
Notes
NEST compatibility: Following NEST
SetStatus()semantics,init_weightis automatically reset to the currentweightafter updates unless explicitly provided in the update dictAll updatable parameters:
weight,delay,delay_steps,tau_Delta,eta,Wmin,Wmax,PI_integral,PI_exp_integral,tau_L_trace,tau_s_trace,t_last_spike_ms,init_weightSign constraint validation occurs after all updates are applied
Examples
Update single parameter:
>>> syn = bp.urbanczik_synapse(weight=1.0) >>> syn.set_status(eta=0.1) >>> print(syn.get('eta')) 0.1
Batch update with dict:
>>> syn.set_status({'weight': 2.0, 'tau_Delta': 120.0}) >>> status = syn.get_status() >>> print(f"Weight: {status['weight']}, tau_Delta: {status['tau_Delta']}") Weight: 2.0, tau_Delta: 120.0
Keyword arguments override dict values:
>>> syn.set_status({'eta': 0.05}, eta=0.08) >>> print(syn.get('eta')) 0.08
- set_weight(weight)[source]#
Update synaptic weight with sign constraint validation.
- Parameters:
weight (
floatorArrayLike) – New synaptic weight. Must be finite scalar and share sign with Wmin/Wmax.- Raises:
ValueError – If weight violates sign constraints or is non-finite/non-scalar.
- simulate_pre_spike_train(pre_spike_times_ms, target, receptor_type=0, multiplicity=1.0, delay=None, delay_steps=None)[source]#
Process sequence of presynaptic spikes and return event history.
Convenience method for batch processing of spike trains. Each spike is processed sequentially using
send(), with synapse state (weight, traces, integrals) updating after each spike.- Parameters:
pre_spike_times_ms (
array_like) – 1D array or sequence of presynaptic spike times in milliseconds. Will be flattened if multidimensional. Order matters: earlier spikes affect later ones through trace dynamics.target (
Any) – Postsynaptic neuron with Urbanczik archiving interface.receptor_type (
intorArrayLike, optional) – Receptor channel index applied to all spikes. Default:0multiplicity (
floatorArrayLike, optional) – Spike multiplicity applied to all spikes. Default:1.0delay (
floatorArrayLike, optional) – Override dendritic delay for all spikes (milliseconds). Default:Nonedelay_steps (
intorArrayLike, optional) – Override event delivery delay for all spikes (steps). Default:None
- Returns:
List of spike event dictionaries, one per input spike, in chronological order. Each dict has same structure as
send()return value.- Return type:
list[dict[str,Any]]
Notes
Stateful processing: Synapse internal state (weight, traces) persists across spikes in the train. Final state reflects cumulative plasticity effects.
Performance: For large spike trains (>10000 spikes), consider batching or vectorization depending on target neuron implementation.
Temporal ordering: Input spikes should typically be sorted in ascending time order for biologically realistic plasticity dynamics.
Examples
Process spike train:
>>> syn = bp.urbanczik_synapse(weight=1.0, eta=0.05) >>> spike_times = [10.0, 15.0, 20.0, 25.0] >>> events = syn.simulate_pre_spike_train(spike_times, target=mock_neuron) >>> weights = [e['weight'] for e in events] >>> print(f"Weight trajectory: {weights}") Weight trajectory: [1.002, 1.005, 1.008, 1.011]
Extract trace evolution:
>>> import numpy as np >>> spike_times = np.arange(0, 100, 5.0) >>> events = syn.simulate_pre_spike_train(spike_times, target=mock_neuron) >>> tau_L_traces = [e['tau_L_trace_post'] for e in events] >>> print(f"Final tau_L trace: {tau_L_traces[-1]:.3f}") Final tau_L trace: 2.456
See also
sendProcess single spike with full control over parameters.
- to_spike_event(t_spike_ms, target, receptor_type=0, multiplicity=1.0, delay=None, delay_steps=None)[source]#
Alias for
send()method.This method provides an alternative API name for spike event generation, matching common naming conventions in event-based simulators.
- Parameters:
t_spike_ms (
floatorArrayLike) – Presynaptic spike time in milliseconds.target (
Any) – Postsynaptic neuron with Urbanczik archiving interface.receptor_type (
intorArrayLike, optional) – Receptor channel index. Default:0multiplicity (
floatorArrayLike, optional) – Spike event multiplicity. Default:1.0delay (
floatorArrayLike, optional) – Override dendritic delay (milliseconds). Default:None(useself.delay)delay_steps (
intorArrayLike, optional) – Override event delivery delay (steps). Default:None(useself.delay_steps)
- Returns:
Spike event dictionary identical to
send()return value.- Return type:
dict[str,Any]
See also
sendPrimary spike processing method with full documentation.