How to set custom delay for excitatory-excitatory connctions

Description of problem

Hi everyone.
I am looking to implement a synaptic delay according this excerpt from a paper:

Every inhibitory neuron is connected to all other neurons in the network. The
delay of each excitatory-excitatory synapse is sampled at random from the [1, 20]ms interval, and the delay of all other synapses is fixed at 1 ms.

However, I can’t find out how to do this on Brian2’s docs, other than not setting the delay in the dictionary.

Minimal code to reproduce problem

import matplotlib.pyplot as plt
import numpy as np

from brian2 import NeuronGroup, Synapses, SpikeMonitor, StateMonitor
from brian2 import ms, mV
from brian2 import defaultclock, run

tfinal = 1000 * ms
Ne = 800
Ni = 200

re = np.random.uniform(size=Ne)
ri = np.random.uniform(size=Ni)
weights = np.hstack(
    [
        0.5 * np.random.uniform(size=(Ne + Ni, Ne)),
        -np.random.uniform(size=(Ne + Ni, Ni)),
    ]
).T

defaultclock.dt = 1 * ms

eqs = """dv/dt = (0.04*v**2 + 5*v + 140 - u + I + I_noise )/ms : 1
         du/dt = (a*(b*v - u))/ms  : 1
         I : 1
         I_noise : 1
         a : 1
         b : 1
         c : 1
         d : 1
       """

N = NeuronGroup(Ne + Ni, eqs, threshold="v>=30", reset="v=c; u+=d", method="euler")
N.v = -65

N_exc = N[:Ne]
N_inh = N[Ne:]

spikemon = SpikeMonitor(N)
statemon = StateMonitor(N, 'v', record=0, when='after_thresholds')
N_exc.a = 0.02
N_exc.b = 0.2
N_exc.c = -65 + 15 * re**2
N_exc.d = 8 - 6 * re**2

N_inh.a = 0.02 + 0.08 * ri
N_inh.b = 0.25 - 0.05 * ri
N_inh.c = -65
N_inh.d = 2

N_exc.u = "b*v"
N_inh.u = "b*v"

S = Synapses(
    N,
    N,
    "w : 1",
    on_pre={"up": "I += w", "down": "I -= w"},
)
S.connect()
# set delays for excitatory-to-excitatory connections randomly between 1 and 20 ms

exc_to_exc = (S.i < Ne) & (S.j < Ne)

S.delay[exc_to_exc] = np.random.uniform(1, 20, size=np.sum(exc_to_exc)) * ms

# set delays for all other connections to 1 ms

S.delay[~exc_to_exc] = 1 * ms

What you have aready tried

I have tried implementing logic in the dicitonary itself and the code provided above.

Expected output (if relevant)

Actual output (if relevant)

Full traceback of error (if relevant)

File "/home/fiach/Documents/College/Project/test_iz.py", line 169, in <module>
    S.delay[exc_to_exc] = np.random.uniform(1, 20, size=np.sum(exc_to_exc)) * ms
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/groups/group.py", line 405, in __getattr__
    return object.__getattribute__(self, name)
  File "/home/fiach/.local/lib/python3.10/site-packages/brian2/synapses/synapses.py", line 1187, in _get_delay
    raise AttributeError(
AttributeError: Synapses do not have a 'pre' pathway, do not know what 'delay' refers to.

Hello,
I think your error is coming from the way you have written your on_pre argument in Synapses object, I am not sure what you wanted to set there, however to implement different delay values the following implementation works

S = Synapses(N, N, model="w : 1", on_pre='  ', method="euler", dt=defaultclock.dt)

# Connect neurons
S.connect()

# Set delays
S.delay = '1*ms'  
S.delay[N_exc, N_exc] = np.random.uniform(0,20)*ms  # Random delay for excitatory-to-excitatory


run(tfinal)

print(S.delay[N_exc, N_exc])

Since I am not sure what you want your on_pre argument to be, I cannot help with what way of writing the on_pre argument would be correct. But I hope this helps a little!

1 Like

Hi @foneill,
you get the error message, because you have two pathways, an up and a down pathway, apparently to create a rectangular synaptic current. The exact code depends on the “width” of your rectangular synaptic current, e.g. for 2ms, I’d write it as:

# set delays for excitatory-to-excitatory connections randomly between 1 and 20 ms
delays = np.random.uniform(1, 20, size=Ne*Ne) * ms
S.up.delay[:Ne, :Ne] = delays
S.down.delay[:Ne, :Ne] = delays + 2*ms

# set delays for all other connections to 1 ms
S.up.delay[Ne:, Ne:] = 1 * ms
S.down.delay[Ne:, Ne:] = 1 * ms + 2 * ms

Let me know if anything is still unclear!

The syntax suggested by @bindok works as well, i.e. to set with S.up.delay[N_exc, N_exc] = ... instead of with S.up.delay[:Ne, :Ne] – the two ways of writing this are equivalent. But note that np.random.uniform(0, 20)*ms would set all connections to the same random value.