Cant use timeArrays stored in a list with PoissonGroups

Description of problem

I want to have several Groups, each receiving input from several PoissonGroups. My plan was to store the groups in a list and the TimeArrays in a list, and then iterate through the Groups and TimeArray lists, connecting them.

However, it seems that in order to provide the timeArray as the argument to the PoissonGroup, you specify it as a string. This works fine when you have a single TimeArray, but once they are stored in a list, I get errors.

How can I pass the stored timeArrays to the PoissonGroup?

Minimal code to reproduce problem

import brian2 as br2
import brian2.numpy_ as np

tau = 10*br2.ms
eqs = '''
dv/dt = (1-v)/tau : 1
'''

rng = np.random.default_rng()

n_cells = 2
group = br2.NeuronGroup(n_cells, eqs)

n_inputs = 3

stimuli = [ ]
for _ in range(n_inputs):
	stimuli.append( br2.TimedArray(rng.random(5)*400*br2.Hz, dt = 100*br2.ms) )

poissons = []
for i in range(n_inputs):
	stimuli_str = 'stimuli[' + str(i) + '](t)'
	poissons.append( br2.PoissonGroup(n_cells, rates=stimuli_str) )


synapses = []
for p in poissons:
	s = br2.synapses.synapses.Synapses(p, group, 'w : 1', on_pre='v += w')
	s.connect(j='i')
	s.w = rng.random(1)*0.1
	synapses.append(s)


net = br2.Network(br2.collect())
items = [synapses, poissons]
net.add(items)		


run_length = 500
net.store()
net.run(run_length*br2.ms)

What you have already tried

I’ve tried adding the list to the net.add(items) call.

Expected output (if relevant)

Actual output (if relevant)

Full traceback of error (if relevant)

Traceback (most recent call last):
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\core\network.py”, line 892, in before_run
obj.before_run(run_namespace)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\input\poissongroup.py”, line 115, in before_run
variables = self.resolve_all(identifiers,
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\groups\group.py”, line 731, in resolve_all
resolved[identifier] = self._resolve(identifier,
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\groups\group.py”, line 691, in _resolve
return self._resolve_external(identifier, run_namespace=run_namespace)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\groups\group.py”, line 814, in _resolve_external
raise KeyError(error_msg)
KeyError: ‘The identifier “stimuli” could not be resolved.’

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File “brain_pi2-prob.py”, line 44, in
net.run(run_length*br2.ms)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\core\base.py”, line 293, in device_override_decorated_function
return func(*args, **kwds)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\units\fundamentalunits.py”, line 2428, in new_f
result = f(*args, **kwds)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\core\network.py”, line 1006, in run
self.before_run(namespace)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\core\base.py”, line 293, in device_override_decorated_function
return func(*args, **kwds)
File “C:\Users\wmkc\AppData\Local\Programs\Python\Python38\lib\site-packages\brian2\core\network.py”, line 894, in before_run
raise BrianObjectException(“An error occurred when preparing an object.”, obj) from ex
brian2.core.base.BrianObjectException: Error encountered with object named ‘poissongroup’.
Object was created here (most recent call only, full details in debug log):
File ‘brain_pi2-prob.py’, line 26, in
poissons.append( br2.PoissonGroup(n_cells, rates=stimuli_str) )

An error occurred when preparing an object. (See above for original error message and traceback.)

Hi @bill-connelly . Objects like NeuronGroup, PoissonGroup, etc. – we call them “Brian objects” – need to be added to the network, but a TimedArray is not a Brian object. Instead, it is a function, and functions use the same mechanism as external constants, i.e. the namespace mechanism. Also, Brian code in strings is not general Python code, you cannot use list indexing and other syntax features.
A quick way to make your example code work would be to replace the PoissonGroup creation by this:

poissons = []
for i in range(n_inputs):
	poissons.append( br2.PoissonGroup(n_cells, rates='stimuli(t)',
	                                  namespace={'stimuli': stimuli[i]}) )

That said, in general you should avoid having multiple objects doing similar things if possible. E.g. by using a single TimedArray with a 2d matrix for the stimulus, your example can work with a single PoissonGroup and a single Synapses object:

stimuli = br2.TimedArray(rng.random((5, n_inputs))*400*br2.Hz, dt = 100*br2.ms)
poisson = br2.PoissonGroup(n_cells*n_inputs, rates='stimuli(t, i//n_cells)')

synapses = br2.Synapses(poisson, group, 'w : 1', on_pre='v += w')
synapses.connect(j='i % n_cells')
synapses.w = np.repeat(rng.random(n_inputs)*0.1, n_cells)

(I quickly wrote this down, I might have translated some details incorrectly).
Presumably your actual code is more complex than this example, but usually you can make things work with very few objects, and this makes the simulation much more efficient.

Hope that helps!