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:
ModulePer-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.
SynapticDelayis a plainnn.Modulethat 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; annealsigmatoward 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 lastmax_delay + 1inputs. Callreset_delay()at the start of each sequence, exactly as you callLeaky.reset_mem().Sequence mode (
step_mode=False) – input(time, batch, channels)(or(time, channels)), the whole sequence at once, to pair withsnntorch.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 asbetabeing 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-Cvector (one learnable delay per channel). IfNone, a single delay value is shared by all channels. Defaults to None.learn_delay (bool, optional) – if
True, the delay is annn.Parameterand 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. IfFalseit 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 owndelaysteps. The firstceil(delay)step-mode outputs after areset_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
Moduleinstance afterwards instead of this since the former takes care of running the registered hooks while the latter silently ignores them.