How to remove duplicated connections between two synapses

I’m trying to make sure two sets of synapses, e.g. S_e2i and S_i2e, are mutually exclusive. In another word, the “target” and “source” of S_e2i will not show up as the “source” and “target” in the S_i2e.
What was done is to use Brian command to generate connections for each sets, then loop over each element of source and target to remove duplicated. The step of removing the duplicate is very slow. Is there a way to speed it up? The following is the snippet of the example.

Thanks

                    #spatial connection e2i
                    self.S_e2i[i].connect(p='exp(-((x_pre-x_post)**2 + (y_pre-y_post)**2)/(2*(sigma)**2))', skip_if_invalid=True, namespace={'sigma':self.sigma_e2i})
                    self.S_e2i[i].max_delay = self.max_delay
                    self.S_e2i[i].delay = 'rand()* max_delay'
                    # get the connection matrix of this S_e2i[i]
                    matrix_S_e2i = np.zeros([len(self.exci_neurons[i]), len(self.inhib_neurons[i])], dtype=bool)
                    matrix_S_e2i[self.S_e2i[i].i[:], self.S_e2i[i].j[:]] = True
                    source_e2i, target_e2i = matrix_S_e2i.nonzero()
                    
                    #spatial connection i2e
                    self.S_i2e_temp[i].connect(p='exp(-((x_pre-x_post)**2 + (y_pre-y_post)**2)/(2*(sigma)**2))', skip_if_invalid=True, namespace={'sigma':self.sigma_i2e})
                    self.S_i2e_temp[i].active = False   # it's only a step-stone for the S_i2e and should be inactive
                    # get the connection matrix of this S_i2e_temp[i] which include S_e2i connection between the same neurons pairs
                    matrix_S_i2e = np.zeros([len(self.inhib_neurons[i]), len(self.exci_neurons[i])], dtype=bool)
                    matrix_S_i2e[self.S_i2e_temp[i].i[:], self.S_i2e_temp[i].j[:]] = True
                    ss, tt = matrix_S_i2e.nonzero()
                    # now make sure e->i and i->e connection are mutually exclusive, i.e. for one connection from e->i, there's no i->e connection from the same meuron ID
                    print("..... removing overlapping connection between e2i and i2e")
                    source_i2e = []
                    target_i2e = []
                    for idx_pre, idx_post in zip(ss, tt):
                        if (idx_pre, idx_post) not in zip(target_e2i, source_e2i):  # note: the source and target of e2i and i2e are opposite
                            source_i2e.append(idx_pre)
                            target_i2e.append(idx_post)

Interesting problem! This should actually be quite fast with some builtin Python data structures and does not require creating matrices as an intermediate step. After having created S_e2i and S_i2e_temp, you can do:

# Get sets with all the (i, j) pairs
e2i_all = set(zip(S_e2i.i, S_e2i.j))
# (inverted indices for i2e)
i2e_all = set(zip(S_i2e_temp.j, S_i2e_temp.i))
# Get the i2e pairs that are not in e2i
i2e_unique = i2e_all - e2i_all
# Get the source and target indices for the unique connections
i_unique, j_unique = zip(*sorted(i2e_unique))

You can then use these for the final connections:

S_i2e = Synapses(...)
S_i2e.connect(i=i_unique, j=j_unique)

Thanks Marcel. Just to make sure. Is the ‘sort’ necessary. Can it just be:

 i_unique, j_unique = zip(*i2e_unique)

It is perfectly fine without the sort as well. I only put it there for consistency with the way that Brian generates synapses. There might be a minor performance improvement with the sorted synapses in some cases, but I doubt it will make a measurable difference here.

I see. It seems to swap the target and source between S_i2e_temp and S_i2e. I intended to keep them same.

Should it be in this way instead?

e2i_all = set(zip(S_e2i.j, S_e2i.i))
i2e_all = set(zip(S_i2e_temp.i, S_i2e_temp.j))

i2e_unique = i2e_all - e2i_all

i_unique, j_unique = zip(*sorted(i2e_unique))

Indeed, sorry about that. Your version should work or you could swap i_unique and j_unique in the end of my version.

1 Like