ht_synapse#
- class brainpy.state.ht_synapse(weight=1.0, delay_steps=1, tau_P=500.0, delta_P=0.125, P=1.0, name=None)#
NEST-compatible Hill-Tononi synapse with vesicle-pool depression.
Implements the short-term depression model from Hill & Tononi (2005) used to simulate thalamocortical sleep/wake dynamics. The model tracks a normalized vesicle pool \(P \in [0,1]\) that recovers exponentially toward 1 and depletes multiplicatively on each presynaptic spike. The effective synaptic weight is the baseline weight scaled by the current pool availability.
This implementation replicates NEST’s
ht_synapse.{h,cpp}connection model exactly, including event ordering, default values, and numerical precision.1. Mathematical Model
The vesicle pool evolves according to:
\[\frac{dP}{dt} = \frac{1 - P}{\tau_P}\]where \(\tau_P\) is the recovery time constant (milliseconds).
2. Spike Processing
When a spike arrives at time \(t\), with the last spike at \(t_{\text{last}}\):
Recover pool (exponential relaxation from \(P_{\text{old}}\) toward 1):
\[P_{\text{send}} = 1 - (1 - P_{\text{old}}) \exp\left(-\frac{t - t_{\text{last}}}{\tau_P}\right)\]Emit event with depression-modulated weight:
\[w_{\text{eff}} = w \cdot P_{\text{send}}\]Deplete pool by fractional amount \(\delta_P \in [0,1]\):
\[P_{\text{new}} = (1 - \delta_P) P_{\text{send}}\]Update last spike time:
\[t_{\text{last}} \leftarrow t\]
This ordering (recover → emit → deplete → update) matches NEST exactly and differs from some formulations that deplete before recovery.
3. Implementation Notes
Timing precision: Uses grid-aligned spike times; sub-grid offsets are dftype = brainstate.environ.dftype() ignored (consistent with NEST’s non-precise-timing variant).
Initial state: Pool starts at \(P = 1.0\), last spike time at 0.0 ms.
Delay handling: Connections store
delay_steps ≥ 1; bothdelayanddelay_stepskeys are accepted and synchronized inset_status.Depletion semantics: \(\delta_P = 0.125\) means each spike removes 12.5% of the current pool, not a fixed decrement.
4. Computational Characteristics
Complexity: \(O(1)\) per spike; exponential evaluation via
math.exp.Stability: Numerically stable for \(\tau_P > 0\) and bounded \(\delta_P \in [0,1]\); no risk of negative pools.
Comparison with NEST: Direct equivalence when using identical parameters, time step, and spike trains. Floating-point differences \(< 10^{-12}\).
- Parameters:
weight (
float,ArrayLike, optional) – Baseline synaptic weight (dimensionless). Can be positive (excitatory) or negative (inhibitory). Default:1.0.delay_steps (
int,ArrayLike, optional) – Transmission delay in integer simulation steps. Must satisfydelay_steps ≥ 1. Default:1.tau_P (
float,ArrayLike, optional) – Vesicle pool recovery time constant (milliseconds). Must be strictly positive. Controls how quickly the pool refills toward 1.0 after depletion. Larger values → slower recovery → stronger depression. Default:500.0ms.delta_P (
float,ArrayLike, optional) – Fractional pool depletion per spike, dimensionless. Must satisfy \(0 \leq \delta_P \leq 1\). Value of 0 disables depression; 1 fully depletes the pool. Default:0.125(12.5% depletion).P (
float,ArrayLike, optional) – Initial pool availability, dimensionless. Must satisfy \(0 \leq P \leq 1\). Typically initialized to 1.0 (fully available). Default:1.0.name (
str, optional) – Optional instance identifier for debugging and logging. Default:None.
Parameter Mapping
brainpy.state
NEST
Description
weightweightBaseline weight
delay_steps/delaydelayTransmission lag
tau_Ptau_PRecovery τ (ms)
delta_Pdelta_PDepletion fraction
PP(internal state)Pool availability
t_last_spike_mst_lastspike_(internal)Last spike time
- Raises:
If
weight,tau_P,delta_P, orPis non-scalar or non-finite. - Ifdelay_steps < 1or is non-integer-valued. - Iftau_P ≤ 0(recovery time must be positive). - Ifdelta_PorPis outside \([0, 1]\). - Ifmultiplicity < 0insend()method.
Examples
Basic usage with single connection:
>>> import brainpy.state as bst >>> syn = bst.ht_synapse(weight=2.5, tau_P=300.0, delta_P=0.2, P=1.0) >>> syn.get_status() {'weight': 2.5, 'delay_steps': 1, 'tau_P': 300.0, 'delta_P': 0.2, 'P': 1.0, 't_last_spike_ms': 0.0, ...}
Simulate spike train and observe depression:
>>> spike_times = [10.0, 20.0, 30.0, 40.0] # milliseconds >>> events = syn.simulate_spike_train(spike_times) >>> for evt in events: ... print(f"t={evt['t_spike_ms']:.1f}ms, w_eff={evt['weight']:.3f}, " ... f"P_send={evt['P_send']:.3f}, P_post={evt['P_post']:.3f}") t=10.0ms, w_eff=2.467, P_send=0.987, P_post=0.790 t=20.0ms, w_eff=2.021, P_send=0.809, P_post=0.647 t=30.0ms, w_eff=1.692, P_send=0.677, P_post=0.541 t=40.0ms, w_eff=1.442, P_send=0.577, P_post=0.462
Update parameters dynamically:
>>> syn.set_status(delta_P=0.5, tau_P=200.0) >>> syn.get('delta_P') 0.5
Reset pool state mid-simulation:
>>> syn.reset_state(P=0.3, t_last_spike_ms=50.0) >>> syn.P 0.3
Process individual spike with custom delay:
>>> event = syn.send(t_spike_ms=100.0, delay_steps=5, receptor_type=1) >>> event['delay_steps'], event['receptor_type'] (5, 1)
See also
ht_neuronHill-Tononi neuron model with intrinsic adaptation.
tsodyks_synapseAlternative Tsodyks-Markram STP with facilitation + depression.
quantal_stp_synapseVesicle-based STP with stochastic release.
Notes
Differences from other STP models:
Tsodyks-Markram (
tsodyks_synapse): Includes facilitation via \(u\) variable; more parameters but richer dynamics.Quantal STP (
quantal_stp_synapse): Discrete vesicle counts with stochastic release; this model uses continuous \(P\).ht_synapse: Simpler, purely depressing model optimized for large-scale thalamocortical simulations (Hill & Tononi 2005).
Biological interpretation:
\(P\) represents the fraction of readily releasable vesicles.
\(\tau_P = 500\) ms captures slow vesicle replenishment typical of depressing cortical synapses.
\(\delta_P = 0.125\) corresponds to ~12.5% vesicle release per spike.
Numerical considerations:
For very short inter-spike intervals \(\Delta t \ll \tau_P\), the exponential term \(\exp(-\Delta t / \tau_P) \approx 1 - \Delta t / \tau_P\), so recovery is approximately linear.
For \(\Delta t \gg \tau_P\), pool fully recovers to \(P \to 1\).
Model is stable for all physically meaningful parameter ranges.
NEST compatibility:
Direct equivalence to NEST 3.x
ht_synapsemodel (C++ implementation).All default values match NEST defaults exactly.
Event ordering and state updates identical to NEST’s
send()method.Does not support NEST’s precise spike timing (
*_psvariants).
References
- get(key='status')[source]#
Retrieve a specific parameter or complete status dictionary.
- Parameters:
key (
str, optional) – Parameter name to retrieve. Special value'status'returns the complete status dictionary. Default:'status'.- Returns:
If
key == 'status', returnsdict[str, Any]fromget_status(). Otherwise, returns the value of the requested parameter (type depends on parameter:float,int,bool).- Return type:
Any- Raises:
KeyError – If
keyis not'status'and is not present in the status dictionary.
Examples
>>> syn = bst.ht_synapse(weight=2.0, tau_P=400.0) >>> syn.get('tau_P') 400.0 >>> syn.get('P') 1.0 >>> syn.get('status') # Full dictionary {'weight': 2.0, 'tau_P': 400.0, 'P': 1.0, ...}
>>> syn.get('nonexistent_key') KeyError: 'Unsupported key "nonexistent_key" for ht_synapse.get().'
- get_status()[source]#
Retrieve complete connection state and parameters.
- Returns:
Dictionary containing all synapse state and metadata:
'weight': float — Baseline synaptic weight.'delay_steps': int — Transmission delay (simulation steps).'delay': int — Alias ofdelay_stepsfor NEST compatibility.'tau_P': float — Pool recovery time constant (ms).'delta_P': float — Fractional depletion per spike [0,1].'P': float — Current pool availability [0,1].'t_last_spike_ms': float — Last processed spike time (ms).'size_of': int — Memory footprint in bytes (Python object size).'has_delay': bool — Delay support flag (alwaysTrue).'supports_wfr': bool — Waveform relaxation flag (False).'is_primary': bool — Primary connection flag (True).'supports_hpc': bool — HPC support flag (True).'supports_lbl': bool — Label-based connectivity flag (True).
- Return type:
dict[str,Any]
Notes
Mimics NEST’s
GetStatusfor connections. All numeric state is converted to native Python types (float,int) for serialization safety.Examples
>>> syn = bst.ht_synapse(weight=2.0, tau_P=400.0) >>> status = syn.get_status() >>> status['tau_P'] 400.0 >>> syn.send(t_spike_ms=10.0) >>> syn.get_status()['P'] # Pool state after spike 0.875
- property properties: dict[str, Any]#
Model capability flags mirroring NEST connection properties.
- Returns:
Dictionary with keys:
'has_delay'boolWhether connection supports transmission delay (always
True).
'supports_wfr'boolWhether model supports waveform relaxation (always
False).
'is_primary'boolWhether this is a primary connection type (always
True).
'supports_hpc'boolWhether model supports high-performance computing features (
True).
'supports_lbl'boolWhether model supports label-based connectivity (
True).
- Return type:
dict[str,Any]
Notes
These flags match NEST’s synapse model introspection API and are used for compatibility checking in network construction tools.
- recover_pool(t_spike_ms)[source]#
Advance pool state to specified time via exponential recovery.
Updates the internal pool variable
Pby integrating the recovery ODE fromt_last_spike_mstot_spike_ms, without depletion. This is used internally bysend()before emitting a spike event.- Parameters:
t_spike_ms (
float,ArrayLike) – Target time in milliseconds. Must be ≥t_last_spike_msfor physical consistency, though negative intervals are mathematically allowed (interpreted as backward recovery, producing \(P < P_{\text{old}}\)).- Returns:
Updated pool availability \(P \in [0, 1]\) after recovery.
- Return type:
Notes
Mathematical formula:
\[P_{\text{new}} = 1 - (1 - P_{\text{old}}) \exp\left(-\frac{\Delta t}{\tau_P}\right)\]where \(\Delta t = t_{\text{spike}} - t_{\text{last}}\).
Side effects:
Modifies
self.Pin-place.Does not update
t_last_spike_ms(caller’s responsibility).Does not deplete the pool (use
send()for full spike processing).
Examples
>>> syn = bst.ht_synapse(tau_P=200.0, P=0.5) >>> syn.t_last_spike_ms = 0.0 >>> P_recovered = syn.recover_pool(t_spike_ms=100.0) >>> P_recovered 0.696734... >>> syn.P # State updated in-place 0.696734... >>> syn.t_last_spike_ms # NOT updated by recover_pool 0.0
See also
sendFull spike processing (recovery + depletion + time update).
- reset_state(P=1.0, t_last_spike_ms=0.0)[source]#
Reset internal state variables to specified values.
Useful for initializing or reinitializing the synapse mid-simulation without recreating the object.
- Parameters:
- Raises:
If
Pis not a finite scalar in [0, 1]. - Ift_last_spike_msis not a finite scalar.
Notes
This method does not reset parameters (
weight,tau_P,delta_P,delay_steps). Useset_status()for parameter updates.Examples
>>> syn = bst.ht_synapse() >>> syn.send(t_spike_ms=10.0) # Depletes pool >>> syn.P 0.875 >>> syn.reset_state(P=1.0, t_last_spike_ms=0.0) # Restore initial state >>> syn.P 1.0
- send(t_spike_ms, receptor_type=0, multiplicity=1.0, delay_steps=None)[source]#
Process incoming presynaptic spike and return emitted event payload.
Implements the full Hill-Tononi spike transmission protocol: recover pool, emit depression-modulated spike, deplete pool, update timestamp. Event ordering matches NEST’s
ht_synapse::send()exactly.- Parameters:
t_spike_ms (
float,ArrayLike) – Spike arrival time in milliseconds (grid-aligned).receptor_type (
int,ArrayLike, optional) – Target receptor port identifier (non-negative integer). Passed through to the event payload without modification. Default:0.multiplicity (
float,ArrayLike, optional) – Spike event multiplicity (non-negative scalar). Scales the effective weight in the event payload. Default:1.0.delay_steps (
int,ArrayLike, optional) – Transmission delay override in simulation steps (must be ≥ 1). IfNone, uses the synapse’s defaultdelay_steps. Default:None.
- Returns:
Spike event payload with keys:
'weight'floatEffective synaptic weight =
weight * P_send * multiplicity.
'delay_steps'intTransmission delay (steps).
'delay'intAlias of
delay_stepsfor NEST compatibility.
'receptor_type'intTarget receptor port (passed through).
'multiplicity'floatEvent multiplicity (passed through).
't_spike_ms'floatSpike time (milliseconds).
'P_send'floatPool availability before depletion [0, 1].
'P_post'floatPool availability after depletion [0, 1].
- Return type:
dict[str,Any]- Raises:
If
t_spike_msis not a finite scalar. - Ifreceptor_typeis not a non-negative integer scalar. - Ifmultiplicityis not a non-negative scalar. - Ifdelay_stepsoverride is provided but is not an integer ≥ 1.
Notes
Execution order (NEST-compatible):
Recover pool to time
t_spike_ms:\[P_{\text{send}} = 1 - (1 - P_{\text{old}}) \exp\left(-\frac{t - t_{\text{last}}}{\tau_P}\right)\]Compute effective weight:
\[w_{\text{eff}} = w \cdot P_{\text{send}} \cdot \text{multiplicity}\]Deplete pool:
\[P_{\text{new}} = (1 - \delta_P) P_{\text{send}}\]Update last spike time:
\[t_{\text{last}} \leftarrow t\]
State updates:
Modifies
self.P(pool availability) in-place.Modifies
self.t_last_spike_msin-place.
Event interpretation:
The returned
weightincorporates depression but not delay.The caller (network simulation engine) is responsible for queueing the event with the specified
delay_stepsoffset.
Examples
Single spike processing:
>>> syn = bst.ht_synapse(weight=2.0, tau_P=300.0, delta_P=0.2) >>> event = syn.send(t_spike_ms=10.0) >>> event['weight'] # Full weight (pool fully available) 2.0 >>> event['P_send'] 1.0 >>> event['P_post'] # Depleted by 20% 0.8
Rapid spike train (depression accumulates):
>>> syn.reset_state() # Start fresh >>> e1 = syn.send(t_spike_ms=0.0) >>> e2 = syn.send(t_spike_ms=10.0) # Insufficient recovery time >>> e1['weight'], e2['weight'] (2.0, 1.627...) # Second spike depressed
Custom delay and receptor:
>>> event = syn.send(t_spike_ms=50.0, delay_steps=5, receptor_type=2) >>> event['delay_steps'], event['receptor_type'] (5, 2)
See also
to_spike_eventAlias of this method for event-style APIs.
simulate_spike_trainProcess multiple spikes in sequence.
recover_poolPool recovery without depletion (internal use).
- set_delay(delay)[source]#
Update the transmission delay (alias for
set_delay_steps).- Parameters:
delay (
int,ArrayLike) – New delay in integer simulation steps, must be ≥ 1.- Raises:
ValueError – If
delayis not an integer-valued scalar ≥ 1.
Notes
This method is provided for NEST compatibility. Internally, it updates the same
delay_stepsattribute asset_delay_steps().Examples
>>> syn = bst.ht_synapse(delay_steps=1) >>> syn.set_delay(5) >>> syn.delay_steps 5
- set_delay_steps(delay_steps)[source]#
Update the transmission delay in simulation steps.
- Parameters:
delay_steps (
int,ArrayLike) – New delay in integer simulation steps, must be ≥ 1.- Raises:
ValueError – If
delay_stepsis not an integer-valued scalar ≥ 1.
Examples
>>> syn = bst.ht_synapse(delay_steps=1) >>> syn.set_delay_steps(3) >>> syn.delay_steps 3
- set_status(status=None, **kwargs)[source]#
Update connection parameters and state from dictionary or kwargs.
- Parameters:
status (
dict[str,Any], optional) – Dictionary of parameter updates. Keys matchget_status()output. IfNone, onlykwargsare applied. Default:None.**kwargs – Additional parameter updates as keyword arguments. Values here override any conflicting keys in
status.
- Raises:
If
delayanddelay_stepsare both provided but differ. - If any parameter value fails validation (see__init__docstring).
Notes
Delay parameter handling:
Both
delayanddelay_stepsare accepted as aliases.If both are provided and differ, raises
ValueError.Internally, both map to the same
delay_stepsattribute.
Validation:
All numeric updates are validated (finite, correct range, scalar).
State variables (
P,t_last_spike_ms) can be updated to reset the synapse mid-simulation.
Examples
>>> syn = bst.ht_synapse(weight=1.0, tau_P=500.0) >>> syn.set_status({'weight': 2.5, 'delta_P': 0.3}) >>> syn.get('weight') 2.5
>>> syn.set_status(tau_P=300.0, P=0.5) # Reset pool mid-simulation >>> syn.tau_P, syn.P (300.0, 0.5)
>>> syn.set_status(delay=3, delay_steps=3) # OK, identical values >>> syn.set_status(delay=2, delay_steps=3) # Raises ValueError ValueError: delay and delay_steps must be identical when both are provided.
- set_weight(weight)[source]#
Update the baseline synaptic weight.
- Parameters:
weight (
float,ArrayLike) – New baseline weight (dimensionless scalar).- Raises:
ValueError – If
weightis not a finite scalar.
Examples
>>> syn = bst.ht_synapse(weight=1.0) >>> syn.set_weight(3.5) >>> syn.weight 3.5
- simulate_spike_train(spike_times_ms, receptor_type=0, multiplicity=1.0, delay_steps=None)[source]#
Process a sequence of spikes and return all emitted events.
Convenience method for simulating a spike train through the synapse and observing depression dynamics over time. Each spike updates the internal state sequentially (recovery + depletion).
- Parameters:
spike_times_ms (
array_like) – Spike times in milliseconds. Can be list, tuple, or array-like. Flattened internally; shape is not preserved. Spikes are processed in the order provided (typically sorted ascending).receptor_type (
int,ArrayLike, optional) – Target receptor port for all events. Default:0.multiplicity (
float,ArrayLike, optional) – Event multiplicity for all events. Default:1.0.delay_steps (
int,ArrayLike, optional) – Delay override for all events (steps). Default:None(use synapse default).
- Returns:
List of spike event payloads, one per input spike. Each dictionary has the structure documented in
send().- Return type:
list[dict[str,Any]]
Notes
State persistence:
Internal state (
P,t_last_spike_ms) is preserved across calls.To start from a known state, call
reset_state()first.
Ordering:
Spikes are processed sequentially in the order they appear in
spike_times_ms. For correct dynamics, times should be sorted.Out-of-order spikes are mathematically allowed but produce non-physical recovery (negative time intervals → pool decreases).
Performance:
\(O(N)\) where \(N\) is the number of spikes.
Each spike requires one
math.exp()evaluation.
Examples
Observe depression over spike train:
>>> syn = bst.ht_synapse(weight=1.0, tau_P=100.0, delta_P=0.25) >>> spike_times = [0, 10, 20, 30, 40] # milliseconds >>> events = syn.simulate_spike_train(spike_times) >>> for evt in events: ... print(f"t={evt['t_spike_ms']:.0f} ms, " ... f"w={evt['weight']:.3f}, P={evt['P_post']:.3f}") t=0 ms, w=1.000, P=0.750 t=10 ms, w=0.821, P=0.616 t=20 ms, w=0.703, P=0.527 t=30 ms, w=0.613, P=0.460 t=40 ms, w=0.542, P=0.406
Reset state between trains:
>>> syn.reset_state(P=1.0, t_last_spike_ms=0.0) >>> events2 = syn.simulate_spike_train([100, 110, 120]) >>> events2[0]['P_send'] # Pool fully recovered 1.0
Custom receptor and delay:
>>> events = syn.simulate_spike_train( ... spike_times_ms=[0, 50], ... receptor_type=2, ... delay_steps=5 ... ) >>> events[0]['receptor_type'], events[0]['delay_steps'] (2, 5)
See also
sendProcess individual spike with full control.
reset_stateReset internal state between simulations.
- to_spike_event(t_spike_ms, receptor_type=0, multiplicity=1.0, delay_steps=None)[source]#
Alias of
send()for event-style APIs.Provided for compatibility with event-driven simulation frameworks that prefer explicit
to_*_eventmethod naming. Functionality is identical tosend().- Parameters:
t_spike_ms (
float,ArrayLike) – Spike arrival time (milliseconds).receptor_type (
int,ArrayLike, optional) – Target receptor port. Default:0.multiplicity (
float,ArrayLike, optional) – Event multiplicity. Default:1.0.delay_steps (
int,ArrayLike, optional) – Delay override (steps). Default:None(use synapse default).
- Returns:
Spike event payload (see
send()for details).- Return type:
dict[str,Any]
See also
sendPrimary spike processing method with full documentation.