Description of problem
Hello Brian2 Team,
I have two different ways of creating multiple synapses between groups, but am wondering why they are not equivalent.
One way (method 1 in code below) creates multiple synapse objects for connections between each subgroup. The other way (method 2 in code below) creates a single synapse object but (I think) creates the same connections as the first method. However, the resulting output behaviour is different and I have no idea why! So I’m just hoping you might be able to shed light on this.
Thank you!
Minimal code to reproduce problem
import brian2 as b2
import numpy as np
b2.seed(100)
### Parameters
l_name = ['2/3E', '2/3I', '4E', '4I'] #neuron subgroup names
n_layer = [10, 10, 10, 10] #number of neurons in each group
N = sum(n_layer) #Total number of neurons
src = ['2/3E', '2/3E', '4E', '4E'] #source groups for connections
tgt = ['2/3E', '2/3I', '4E', '4I'] #target groups for connections
nsyn_list = [3, 5, 7, 9] #number of synapses for each connection
nn_cum = [0]
nn_cum.extend(np.cumsum(n_layer)) #cumulative numbers for subgroup indexes
#Neuron Model
eqs = '''
dv/dt = (I-v)/tau : 1
I : 1
tau : second
'''
#Create & initialise Neurons
neurons = b2.NeuronGroup(N, eqs, threshold='v>1', reset='v = 0', method='exact')
neurons.I = 2
neurons.tau = 10*b2.ms
n_groups = [] # Stores NeuronGroups, one for each population
#Create dictionary of subgroups
for r in range(0, len(n_layer)):
n_groups.append(neurons[nn_cum[r]:nn_cum[r+1]])
n_dict = dict(zip(l_name, n_layer))
syn_group = [] # Stores connections
##### Synapse Creation method 1
# for i in range(len(src)):
# syn_group.append(b2.Synapses(n_groups[list(n_dict.keys()).index(src[i])], n_groups[list(n_dict.keys()).index(tgt[i])], on_pre='v_post += 0.2'))
# nsyn = nsyn_list[i]
# pre_index = np.random.randint(n_dict[src[i]], size=nsyn)
# post_index = np.random.randint(n_dict[tgt[i]], size=nsyn)
# syn_group[-1].connect(i = pre_index, j = post_index)
# syn_group[-1].delay = 1.4*b2.ms
##### Synapse Creation method 2
syn = b2.Synapses(neurons, neurons, on_pre='v_post += 0.2')
for i in range(len(src)):
nsyn = nsyn_list[i]
cum_names = list(n_dict.keys())
cum_vals = [0]
cum_vals.extend(np.cumsum(list(n_dict.values())))
if cum_names.index(src[i]) > 0:
idx1_src = cum_names.index(src[i])
idx2_src = cum_names.index(src[i]) + 1
else:
idx1_src = 0
idx2_src = cum_names.index(src[i]) + 1
if cum_names.index(tgt[i]) > 0:
idx1_tgt = cum_names.index(tgt[i])
idx2_tgt = cum_names.index(tgt[i]) + 1
else:
idx1_tgt = 0
idx2_tgt = cum_names.index(tgt[i]) + 1
pre_index = np.random.randint(min(cum_vals[idx1_src], cum_vals[idx2_src]), max(cum_vals[idx1_src], cum_vals[idx2_src]), size=nsyn)
post_index = np.random.randint(min(cum_vals[idx1_tgt], cum_vals[idx2_tgt]), max(cum_vals[idx1_tgt], cum_vals[idx2_tgt]), size=nsyn)
syn.connect(i = pre_index, j = post_index)
syn.delay = 1.4*b2.ms
syn_group.append(syn)
#Monitor
M = b2.StateMonitor(neurons, 'v', record=True)
#Run
b2.run(100*b2.ms)
#Plotting neuron activity
b2.plot(M.t/b2.ms, M.v[0], label='Neuron 0')
b2.plot(M.t/b2.ms, M.v[1], label='Neuron 1')
b2.xlabel('Time (ms)')
b2.ylabel('v')
#Plotting connectivity
def visualise_connectivity(S):
for n in range(len(S)):
Ns = len(S[n].source)
Nt = len(S[n].target)
b2.figure(figsize=(10, 4))
b2.subplot(121)
b2.plot(np.zeros(Ns), np.arange(Ns), 'ok', ms=10)
b2.plot(np.ones(Nt), np.arange(Nt), 'ok', ms=10)
for i, j in zip(S[n].i, S[n].j):
b2.plot([0, 1], [i, j], '-k')
b2.xticks([0, 1], ['Source', 'Target'])
b2.ylabel('Neuron index')
b2.xlim(-0.1, 1.1)
b2.ylim(-1, max(Ns, Nt))
b2.subplot(122)
b2.plot(S[n].i, S[n].j, 'ok')
b2.xlim(-1, Ns)
b2.ylim(-1, Nt)
b2.xlabel('Source neuron index')
b2.ylabel('Target neuron index')
visualise_connectivity(syn_group)
Expected Output
I would expect the two synapse creation methods to give the same output. The synaptic connections look equivalent in the (visualise connectivity) plots to but the actual behaviour is different.
Actual output (if relevant)
Connectivity looks the same in both methods but the neuron behaviour does not.
Method 2