tsodyks2_synapse#

class brainpy.state.tsodyks2_synapse(weight=1.0, delay=Quantity(1., "ms"), receptor_type=0, U=0.5, u=<object object>, x=1.0, tau_rec=Quantity(800., "ms"), tau_fac=Quantity(0., "ms"), post=None, name=None)#

NEST-compatible tsodyks2_synapse connection model.

Short description

Synapse type with short-term depression and facilitation.

Description

tsodyks2_synapse mirrors NEST models/tsodyks2_synapse.h. The model stores per-connection state variables:

  • x: current scaling factor of synaptic efficacy,

  • u: current release probability,

  • t_lastspike: last presynaptic spike stamp.

Together with fixed model parameters U, tau_rec and tau_fac, incoming spikes are processed in the same order as NEST:

1. State propagation

For h = t_spike - t_lastspike, if this is not the first spike (i.e. t_lastspike >= 0), propagate state to the current spike:

\[ \begin{align}\begin{aligned}x \leftarrow 1 + (x - xu - 1)e^{-h/\tau_{rec}}\\u \leftarrow U + u(1-U)e^{-h/\tau_{fac}}\end{aligned}\end{align} \]

with the NEST special case tau_fac == 0:

\[e^{-h/\tau_{fac}} \equiv 0\]

2. Effective weight computation

Compute effective synaptic weight for this spike:

\[w_{eff} = x u w\]

3. Event scheduling

Schedule event delivery with inherited static-synapse delay semantics.

4. State update

Set t_lastspike = t_spike.

This model scales the baseline synaptic weight only and is suitable for current- or conductance-based postsynaptic dynamics.

Event timing semantics

As in NEST, updates use spike stamps and ignore precise sub-step offsets. In this backend, each presynaptic event at simulation step t is evaluated at on-grid stamp t + dt.

Mathematical formulation

The tsodyks2_synapse model implements a simplified version of the Tsodyks-Markram short-term plasticity mechanism that tracks only two key dynamic variables rather than the full resource state model.

State variables

  • \(x(t)\) – scaling factor representing available resources

  • \(u(t)\) – utilization (release probability)

  • \(t_{\mathrm{last}}\) – timestamp of last presynaptic spike

Parameters:

  • \(U\) – baseline utilization increment (facilitation magnitude)

  • \(\tau_{rec}\) – recovery time constant (depression timescale)

  • \(\tau_{fac}\) – facilitation time constant

  • \(w\) – baseline synaptic weight

Dynamics:

Upon arrival of a presynaptic spike at time \(t_s\), with inter-spike interval \(h = t_s - t_{\mathrm{last}}\):

  1. If \(t_{\mathrm{last}} \geq 0\) (not the first spike), propagate state variables:

    \[ \begin{align}\begin{aligned}x(t_s) = 1 + [x(t_{\mathrm{last}}) - x(t_{\mathrm{last}})u(t_{\mathrm{last}}) - 1] \exp\left(-\frac{h}{\tau_{rec}}\right)\\u(t_s) = U + u(t_{\mathrm{last}})(1-U)\exp\left(-\frac{h}{\tau_{fac}}\right)\end{aligned}\end{align} \]

    with the special case when \(\tau_{fac} = 0\):

    \[\exp\left(-\frac{h}{\tau_{fac}}\right) \equiv 0\]
  2. Compute effective synaptic strength:

    \[w_{\mathrm{eff}} = x(t_s) \cdot u(t_s) \cdot w\]
  3. Update last spike time:

    \[t_{\mathrm{last}} \leftarrow t_s\]

Biological interpretation:

  • Depression (\(x\) dynamics): After each spike, \(x\) decreases by factor \(xu\), representing depletion of available resources. It recovers exponentially with time constant \(\tau_{rec}\).

  • Facilitation (\(u\) dynamics): The utilization \(u\) increases toward \(U\) after each spike, representing calcium-dependent facilitation of release probability. It decays exponentially with time constant \(\tau_{fac}\).

  • Combined effect: The effective synaptic weight \(w_{\mathrm{eff}}\) is the product of available resources (\(x\)), release probability (\(u\)), and baseline weight (\(w\)).

Parameter regimes:

  • Depression-dominated: \(\tau_{rec} \gg \tau_{fac}\), \(U\) moderate. Resources deplete faster than facilitation builds up.

  • Facilitation-dominated: \(\tau_{fac} \gg \tau_{rec}\), \(U\) small. Facilitation accumulates across multiple spikes.

  • Pure depression: \(\tau_{fac} = 0\), fixed \(u = U\). No facilitation dynamics.

Computational considerations

  • Event-driven updates: State propagation occurs only at spike times, making the model computationally efficient for sparse spike trains.

  • Exponential approximation: For very small \(h\), numerical precision may be limited. NEST uses the convention that \(\tau_{fac} = 0\) exactly eliminates facilitation rather than using a threshold.

  • First spike handling: The condition \(t_{\mathrm{last}} < 0\) distinguishes the first spike, which uses initial \(x\) and \(u\) values directly without propagation.

  • Multiplicative weight scaling: Unlike tsodyks_synapse, this model does not track postsynaptic current dynamics separately. The effective weight \(w_{\mathrm{eff}}\) directly modulates the delivered event.

Comparison with tsodyks_synapse

The tsodyks2_synapse model differs from tsodyks_synapse in several ways:

State representation:

  • tsodyks2: Two variables (\(x\), \(u\))

  • tsodyks: Three variables (\(x\), \(y\), \(u\)) plus postsynaptic current \(\tau_{psc}\)

Update equations:

  • tsodyks2: Simplified model where \(x\) represents effective resources after accounting for utilization

  • tsodyks: Full resource model with explicit recovered (\(x\)), active (\(y\)), and inactive (\(z = 1-x-y\)) resource fractions

Postsynaptic dynamics:

  • tsodyks2: No built-in postsynaptic filtering; \(w_{\mathrm{eff}}\) directly scales the event

  • tsodyks: Includes \(\tau_{psc}\) for postsynaptic current dynamics

Use cases:

  • tsodyks2: Simpler, faster, suitable when postsynaptic filtering is handled separately (e.g., by neuron model)

  • tsodyks: More detailed, includes postsynaptic current kinetics in the synapse model

Parameters:
  • weight (float or array-like, optional) – Baseline synaptic weight \(w\). Dimensionless scalar or array. Default: 1.0.

  • delay (float or Quantity, optional) – Synaptic transmission delay. Must be a positive time value in milliseconds. Accepts saiunit.Quantity with time units or float (interpreted as ms). Default: 1.0 * u.ms.

  • receptor_type (int, optional) – Receiver port/receptor identifier for multi-receptor postsynaptic neurons. Non-negative integer. Default: 0.

  • U (float or array-like, optional) – Utilization increment parameter \(U\). Must be in [0, 1]. Controls the magnitude of facilitation per spike. Dimensionless scalar. Default: 0.5.

  • u (float or array-like or UNSET, optional) – Initial release probability \(u(0)\). Must be in [0, 1]. If not provided, defaults to U. Dimensionless scalar. Default: U.

  • x (float or array-like, optional) – Initial scaling factor of synaptic efficacy \(x(0)\). Represents initial available resources. Dimensionless scalar. Typical range [0, 1], though values > 1 are allowed. Default: 1.0.

  • tau_rec (float or Quantity, optional) – Recovery (depression) time constant \(\tau_{rec}\). Must be strictly positive (> 0). Controls the timescale of resource replenishment. Accepts saiunit.Quantity with time units or float (interpreted as ms). Default: 800.0 * u.ms.

  • tau_fac (float or Quantity, optional) – Facilitation time constant \(\tau_{fac}\). Must be non-negative (>= 0). When zero, facilitation is disabled and \(u\) remains constant at \(U\). Accepts saiunit.Quantity with time units or float (interpreted as ms). Default: 0.0 * u.ms.

  • post (object, optional) – Default postsynaptic receiver object. If provided, this receiver will be used by default in send() and update() calls when no explicit receiver is specified. Default: None.

  • name (str, optional) – Optional name identifier for this connection object. Used for debugging and introspection. Default: None.

Parameter Mapping

The following table maps NEST parameter names to brainpy.state equivalents and describes their interpretation:

NEST Parameter

brainpy.state

Type

Description

weight

weight

float

Baseline synaptic weight \(w\)

delay

delay

Quantity (ms)

Transmission delay in milliseconds

receptor_ type

receptor_ type

int

Target receptor port identifier

U

U

float [0,1]

Utilization increment

u

u

float [0,1]

Initial/current release probability

x

x

float

Initial/current efficacy scaling

tau_rec

tau_rec

Quantity (ms)

Recovery time constant

tau_fac

tau_fac

Quantity (ms)

Facilitation time constant

Notes

  • This model transmits spike-like events only. The event_type is fixed to 'spike'.

  • The state variables x, u, and t_lastspike are mutable connection states. Current values can be retrieved via get() and modified via set().

  • When tau_fac == 0 exactly, facilitation is disabled and \(u\) remains constant. This matches NEST’s behavior and avoids numerical issues with \(\exp(-h/0)\).

  • Calling init_state() resets the event queue and restores x, u, and t_lastspike to their configured initial values.

  • The model uses event-driven updates: state propagation occurs only at spike times, not at every simulation timestep.

Raises:
  • ValueError – If U or u is not in the range [0, 1].

  • ValueError – If tau_rec is not strictly positive (<= 0).

  • ValueError – If tau_fac is negative (< 0).

  • ValueError – If any scalar parameter has size != 1 when converted to array.

See also

tsodyks_synapse

Full resource-based STP model with postsynaptic current.

static_synapse

Base class providing delay and weight handling.

tsodyks_synapse_hom

Homogeneous-weight variant of tsodyks_synapse.

References

Examples

Create a depression-dominated synapse (fast recovery, no facilitation):

>>> import brainpy.state as bst
>>> import saiunit as u
>>> syn = bst.tsodyks2_synapse(
...     weight=1.0,
...     delay=1.5 * u.ms,
...     U=0.5,
...     tau_rec=100.0 * u.ms,
...     tau_fac=0.0 * u.ms
... )
>>> syn.init_state()

Create a facilitation-dominated synapse (slow recovery, long facilitation):

>>> syn_fac = bst.tsodyks2_synapse(
...     weight=0.5,
...     U=0.15,
...     tau_rec=800.0 * u.ms,
...     tau_fac=1000.0 * u.ms
... )

Inspect current synapse state:

>>> state = syn.get()
>>> print(f"x={state['x']}, u={state['u']}")
x=1.0, u=0.5

Simulate a spike train and observe depression:

>>> import brainstate as bst
>>> import jax.numpy as jnp
>>> # Create postsynaptic target
>>> class SimpleTarget(bst.nn.Dynamics):
...     def __init__(self):
...         super().__init__()
...         self.input = 0.0
...     def update(self, inp=0.0):
...         self.input = inp
>>> target = SimpleTarget()
>>> syn = bst.tsodyks2_synapse(
...     weight=1.0,
...     delay=0.1 * u.ms,
...     U=0.5,
...     tau_rec=200.0 * u.ms,
...     tau_fac=0.0 * u.ms,
...     post=target
... )
>>> syn.init_state()
>>> # Simulate high-frequency spike train
>>> with bst.environ.context(dt=0.1 * u.ms):
...     for i in range(10):
...         delivered = syn.update(pre_spike=1.0)
...         state = syn.get()
...         print(f"Step {i}: x={state['x']:.3f}, w_eff={state['x']*state['u']:.3f}")
get()[source]#

Return current public parameters and mutable state.

Retrieves a dictionary containing all NEST-compatible parameters and current dynamic state variables. This is useful for introspection, checkpointing, and parameter queries.

Returns:

Dictionary with the following keys:

  • 'weight' : float Baseline synaptic weight.

  • 'delay' : float Synaptic delay in milliseconds.

  • 'receptor_type' : int Target receptor port identifier.

  • 'U' : float Utilization increment parameter (fixed).

  • 'u' : float Current release probability (mutable state).

  • 'x' : float Current efficacy scaling factor (mutable state).

  • 'tau_rec' : float Recovery time constant in milliseconds.

  • 'tau_fac' : float Facilitation time constant in milliseconds.

  • 'synapse_model' : str Model identifier string 'tsodyks2_synapse'.

Return type:

dict

Notes

  • The returned u and x values reflect the current synapse state, which changes after each spike.

  • All time constants are returned as float values in milliseconds, with units stripped.

  • The synapse_model key is included for NEST compatibility and model identification.

Examples

>>> import brainpy.state as bst
>>> import saiunit as u
>>> syn = bst.tsodyks2_synapse(weight=2.0, U=0.4, tau_rec=500*u.ms)
>>> syn.init_state()
>>> params = syn.get()
>>> print(params['U'], params['x'])
0.4 1.0
init_state(batch_size=None, **kwargs)[source]#

Initialize or reset synapse state variables.

Resets the event delivery queue and restores dynamic state variables (x, u, t_lastspike) to their configured initial values. This method should be called before starting a simulation or when reinitializing the model.

Parameters:
  • batch_size (int, optional) – Ignored. Included for API compatibility with batched models.

  • **kwargs (dict, optional) – Additional keyword arguments. Ignored.

Notes

  • The initial values of x and u are determined by the values provided during object construction (or last set() call).

  • t_lastspike is always reset to -1.0 to indicate no prior spike has occurred.

  • The inherited event queue from static_synapse is also cleared.

send(multiplicity=1.0, *, post=None, receptor_type=None)[source]#

Schedule one outgoing event with NEST tsodyks2_synapse dynamics.

Processes a presynaptic spike event by:

  1. Propagating synapse state (x, u) from the last spike time to the current spike time using exponential decay equations.

  2. Computing the effective synaptic weight as \(w_{\mathrm{eff}} = x \cdot u \cdot w \cdot \text{multiplicity}\).

  3. Scheduling delayed delivery of the weighted event to the postsynaptic target.

  4. Updating t_lastspike to the current spike timestamp.

This method implements the exact NEST tsodyks2_synapse update semantics, including the special case for tau_fac == 0.

Parameters:
  • multiplicity (float or array-like, optional) – Presynaptic spike multiplicity. Scales the effective weight linearly. For standard point-neuron spikes, this is 1.0. For population models or weighted spike counts, can be > 1. Default: 1.0.

  • post (object, optional) – Target postsynaptic receiver for this event. If None, uses the default receiver set during construction. Default: None.

  • receptor_type (int or array-like, optional) – Target receptor port for this event. If None, uses the default receptor_type set during construction. Default: None.

Returns:

True if an event was scheduled (multiplicity non-zero), False otherwise.

Return type:

bool

Notes

  • If multiplicity is zero or evaluates to zero after conversion, no event is scheduled and state is not updated.

  • The spike timestamp is computed as current_time + dt, where dt is the simulation timestep. This matches NEST’s on-grid spike timing convention.

  • For the first spike (t_lastspike < 0), state propagation is skipped and initial x and u values are used directly.

  • State variables x and u are updated in place during this call, affecting all subsequent spikes.

  • The effective weight calculation includes the multiplicity factor, so multiple coincident spikes can be represented as a single call with multiplicity > 1.

Examples

>>> import brainpy.state as bst
>>> import saiunit as u
>>> # Create synapse with target
>>> class Target(bst.nn.Dynamics):
...     def __init__(self):
...         super().__init__()
...         self.current = 0.0
>>> target = Target()
>>> syn = bst.tsodyks2_synapse(
...     weight=1.0,
...     delay=1.0 * u.ms,
...     U=0.5,
...     tau_rec=100.0 * u.ms,
...     post=target
... )
>>> syn.init_state()
>>> # Send a spike
>>> with bst.environ.context(dt=0.1 * u.ms):
...     success = syn.send(multiplicity=1.0)
>>> print(success)
True
>>> # Send multiple spikes in quick succession
>>> for i in range(5):
...     syn.send(multiplicity=1.0)
...     state = syn.get()
...     print(f"Spike {i+1}: x={state['x']:.3f}, u={state['u']:.3f}")
set(*, weight=<object object>, delay=<object object>, receptor_type=<object object>, U=<object object>, u=<object object>, x=<object object>, tau_rec=<object object>, tau_fac=<object object>, post=<object object>)[source]#

Set NEST-style public parameters and mutable state.

Updates one or more synapse parameters or state variables. All arguments are keyword-only and optional. Only provided parameters are modified; others retain their current values. New values are validated before assignment.

Parameters:
  • weight (float or array-like, optional) – New baseline synaptic weight. If not provided, unchanged.

  • delay (float or Quantity, optional) – New synaptic delay in milliseconds. Must be positive. If not provided, unchanged.

  • receptor_type (int, optional) – New target receptor port identifier. If not provided, unchanged.

  • U (float or array-like, optional) – New utilization increment parameter. Must be in [0, 1]. If not provided, unchanged.

  • u (float or array-like, optional) – New current release probability. Must be in [0, 1]. If not provided, unchanged.

  • x (float or array-like, optional) – New current efficacy scaling factor. If not provided, unchanged.

  • tau_rec (float or Quantity, optional) – New recovery time constant in milliseconds. Must be > 0. If not provided, unchanged.

  • tau_fac (float or Quantity, optional) – New facilitation time constant in milliseconds. Must be >= 0. If not provided, unchanged.

  • post (object, optional) – New default postsynaptic receiver. If not provided, unchanged.

Raises:

Notes

  • Setting u or x modifies the current synapse state, affecting subsequent spike processing.

  • The initial values (_u0, _x0) are updated to match new u and x values, so init_state() will restore these new values.

  • All validation is performed before any state modification, ensuring atomicity (either all changes succeed or none do).

Examples

>>> import brainpy.state as bst
>>> import saiunit as u
>>> syn = bst.tsodyks2_synapse(weight=1.0, U=0.5)
>>> syn.init_state()
>>> # Modify parameters
>>> syn.set(U=0.3, tau_rec=300.0 * u.ms)
>>> # Modify current state
>>> syn.set(u=0.8, x=0.5)
>>> params = syn.get()
>>> print(params['U'], params['u'], params['x'])
0.3 0.8 0.5
update(pre_spike=0.0, *, post=None, receptor_type=None)[source]#

Deliver due events, then schedule current-step presynaptic input.

Primary update method called at each simulation timestep. Performs two operations in sequence:

  1. Delivery phase: Delivers all events scheduled for the current timestep to their target receivers (accumulated from past spikes with appropriate delays).

  2. Reception phase: Processes current presynaptic input by aggregating spike counts and calling send() to schedule new delayed events.

This method integrates with the event queue system inherited from static_synapse and handles both current and delta input collection via sum_current_inputs() and sum_delta_inputs().

Parameters:
  • pre_spike (float or array-like, optional) – Presynaptic spike input at the current timestep. Represents spike multiplicity (typically 0.0 for no spike, 1.0 for a single spike). Can be > 1 for population models. Default: 0.0.

  • post (object, optional) – Target postsynaptic receiver for new events. If None, uses the default receiver set during construction. Default: None.

  • receptor_type (int or array-like, optional) – Target receptor port for new events. If None, uses the default receptor_type set during construction. Default: None.

Returns:

Number of events delivered to postsynaptic targets during the delivery phase of this timestep.

Return type:

int

Notes

  • The method first delivers events from the queue, then processes new presynaptic input. This ordering ensures causality: events generated at timestep \(t\) are delivered at \(t + d\), where \(d\) is the delay in timesteps.

  • Current inputs are accumulated from multiple sources via sum_current_inputs(), allowing multiple projections to target the same synapse.

  • Delta inputs (instantaneous inputs delivered in the same timestep) are also accumulated via sum_delta_inputs().

  • If the total aggregated input is zero, no new event is scheduled and synapse state remains unchanged.

Examples

>>> import brainpy.state as bst
>>> import saiunit as u
>>> # Create synapse with target
>>> class Target(bst.nn.Dynamics):
...     def __init__(self):
...         super().__init__()
...         self.current = 0.0
...     def update(self, inp=0.0):
...         self.current += inp
>>> target = Target()
>>> syn = bst.tsodyks2_synapse(
...     weight=1.0,
...     delay=1.0 * u.ms,
...     U=0.5,
...     tau_rec=200.0 * u.ms,
...     post=target
... )
>>> syn.init_state()
>>> target.init_state()
>>> # Simulate spike train
>>> with bst.environ.context(dt=0.1 * u.ms):
...     for t in range(20):
...         spike = 1.0 if t in [0, 5, 10] else 0.0
...         n_delivered = syn.update(pre_spike=spike)
...         if n_delivered > 0:
...             print(f"Step {t}: delivered {n_delivered} events")