snn.SynapticDelay

class snntorch._neurons.synapticdelay.SynapticDelay(max_delay, delay=0.0, channels=None, learn_delay=False, kernel='linear', sigma=1.0, step_mode=True)[source]

Bases: Module

Per-channel (axonal / synaptic) transmission delay for spike or current signals, with an optionally learnable delay time.

A spike or current on channel \(c\) is delayed by \(d_c \ge 0\) time steps before it is passed on. SynapticDelay is a plain nn.Module that is dropped into a network wherever a lag is wanted, typically between a connection and a neuron:

self.fc1   = nn.Linear(num_in, num_hidden)
self.delay = snn.SynapticDelay(max_delay=16, delay=1.0,
                               channels=num_hidden, learn_delay=True)
self.lif1  = snn.Leaky(beta=0.9)
...
cur1 = self.delay(self.fc1(x))      # step mode: cur1, x are (batch, N)
spk1, mem1 = self.lif1(cur1, mem1)

The fractional delay is realised by interpolation, so \(d_c\) is a continuous, trainable quantity. Two interpolation kernels are provided:

  • kernel="linear" – a 2-tap kernel, \(w[\lfloor d \rfloor] = 1 - \mathrm{frac}(d)\), \(w[\lfloor d \rfloor + 1] = \mathrm{frac}(d)\). Exactly differentiable w.r.t. \(d\). This is the default.

  • kernel="gaussian" – a normalised \(\exp(-(\tau - d)^2 / 2\sigma^2)\) kernel. Smoother gradients over a wide delay range; anneal sigma toward a small value during training (the DCLS trick, Hammouamri et al., ICLR 2024).

Two call modes, one kernel:

  • Step mode (step_mode=True, the default) – input (batch, channels), called once per time step (the standard snntorch loop). An internal ring buffer holds the last max_delay + 1 inputs. Call reset_delay() at the start of each sequence, exactly as you call Leaky.reset_mem().

  • Sequence mode (step_mode=False) – input (time, batch, channels) (or (time, channels)), the whole sequence at once, to pair with snntorch.LeakyParallel. Implemented as a causal depthwise convolution along time; no state, no reset needed.

Both modes use the same delay kernel and, for the same delay, produce the same output (up to the ring buffer’s ramp-up at the start).

The delay is clamped to [0, max_delay] on every forward pass, in the same spirit as beta being clamped to [0, 1].

Parameters:
  • max_delay (int) – maximum delay in time steps; sets the ring-buffer / convolution-kernel length. Must satisfy max_delay >= ceil(delay).

  • delay (float or torch.Tensor, optional) – initial delay in time steps. Scalar, or a tensor of shape (channels,) for a per-channel initialisation. Defaults to 0.0 (pass-through).

  • channels (int, optional) – number of channels C. If given, the delay is a length-C vector (one learnable delay per channel). If None, a single delay value is shared by all channels. Defaults to None.

  • learn_delay (bool, optional) – if True, the delay is an nn.Parameter and is trained by backprop; otherwise it is a fixed buffer. Defaults to False.

  • kernel (str, optional) – "linear" (2-tap, default) or "gaussian".

  • sigma (float, optional) – width of the Gaussian kernel (ignored for "linear"). Defaults to 1.0.

  • step_mode (bool, optional) – if True (default) the module is called once per time step with (batch, channels) input and keeps a ring buffer. If False it takes a whole (time, batch, channels) sequence at once. Defaults to True.

Inputs: input_
  • input_ of shape (batch, channels) (step mode) or (time, batch, channels) / (time, channels) (sequence mode).

Outputs: delayed
  • delayed – same shape as input_; each channel delayed by its own delay steps. The first ceil(delay) step-mode outputs after a reset_delay() are ramp-up (buffer still filling), as with any real delay line.

Learnable Parameters:
  • SynapticDelay.delay (torch.Tensor) – the per-channel (or scalar) delay, present as a parameter when learn_delay=True.

extra_repr()[source]

Return the extra representation of the module.

To print customized extra information, you should re-implement this method in your own modules. Both single-line and multi-line strings are acceptable.

forward(input_)[source]

Define the computation performed at every call.

Should be overridden by all subclasses.

Note

Although the recipe for forward pass needs to be defined within this function, one should call the Module instance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.

reset_delay()[source]

Clear the step-mode ring buffer. Call once per sequence, like Leaky.reset_mem().