Using spatial coordinates of Poisson group

Description of problem

I’d like to modulate the probability of connection between a Poissongroup and a Neurongroup depending on the respective distance of each neuron.
First, I’ve added an attribute to Poissongroup:

Inputs = PoissonGroup(N_Poisson, rates=0*Hz) # will be set in run_regularly

I give these attribute some values (x, y position around a circle).
I’ve create a Neurongroup:

Pop_Vis_E = NeuronGroup(N_E_vis*Num_column, eqs_E_vis, threshold=‘v > V_thr’,
reset=‘v = V_reset’, refractory=tau_rp_E, method=‘euler’)
They also have x_distant and y_distant attributes.

Then I connect them:
Syn_P2VE=Synapses(Inputs, Pop_Vis_E, model=eqs_glut_poisson, on_pre=eqs_pre_glut, method=‘euler’)
Syn_P2VE.connect(p=’((1/(sqrt(2*pi))) * exp(-0.5 *(sqrt((x_distant_pre-x_distant_post)**2+(y_distant_pre-y_distant_post)**2))**2))’)

The error message is as follows:
KeyError: ‘The identifier “x_distant_pre” could not be resolved.’

It behaves as if it could not use x_distant of the pre object (Inputs) as an attribute.
Do you have any idea what I do wrong here and how I can resolve this question?


1 Like

Hi. Great question, I am sure you are not the first one struggling with this. There is a misunderstanding about what add_attribute does – I’ll give some more information on it below for those that are interested. What you need is a parameter defined as part of equations. PoissonGroup does not allow you to specify its equations, so you cannot use it for this task and have to use NeuronGroup instead. This is not as complicated as it seems, since PoissonGroup is mostly a convenient wrapper around NeuronGroup setting the threshold condition to rand() < rates*dt (if your rate is 100Hz, and dt is 0.1ms, you have a 0.01 or 1% chance of a spike in each time step). So in your example, the definition should be (assuming you are using dimensionless attributes)

Inputs = NeuronGroup(N_Poisson,'''rates : Hz
                                  x_distant : 1 (constant)
                                  y_distant : 1 (constant)''',

If you have a single rate for all neurons, you should also add (shared) after the definition of rates to make clear that it is a single value.

Background for add_attribute

In Brian, parameters of a NeuronGroup are available as attributes of the object. So after the above definition you are allowed to write:

 Inputs.rates = 100*Hz

Now if you make a mistake and write for example:

Inputs.rate = 100*Hz

Brian will raise an error, assuming you made a typo. This is different from standard Python behaviour. For most objects, assigning to an attribute that does not exist will create this attribute:

>>> class C:
...    def __init__(self, value):
...        self.value = value
>>> obj = C(23)
>>> obj.valu = 17  # valu instead of value, no error is raised!
>>> print(obj.value)
>>> print(obj.valu)

There might be legitimate use cases for this even in Brian. E.g. in a complex model you might want to store additional information in a NeuronGroup that is not relevant for the simulation code as such, say store a reference to the paper that proposed a model. Since we want to protect users from making errors that are hard to detect (e.g. the ratesrate typo above), we raise an error if an attribute does not exist:

>>> Inputs.reference = 'Someone et al. (2019), Some paper'
Traceback (most recent call last):
Could not find a state variable with name "reference". Use the add_attribute method if you intend to add a new attribute to the object.

Ass the error message suggests, if you are really sure that you want to add a new attribute (and that you did not just misspell an existing one), you can do this with the add_attribute function:

>>> Inputs.add_attribute('reference')
>>> Inputs.reference = 'Someone et al. (2019), Some paper'
>>> print(Inputs.reference)
Someone et al. (2019), Some paper

But such an attribute will not be accessible to Brian code (e.g. in connect statements), for this is has to be defined as part of equations.


Thanks a lot for taking time to write such a nice response. It helps a lot.

I have a follow up question regarding this fake poisson group. Let’s assume I want to add a baseline of 5 sp/s to

Inputs = NeuronGroup(N_Poisson,'''rates : Hz
                                  x_distant : 1 (constant)
                                  y_distant : 1 (constant)''',

What would be the best strategy?

Not sure that I understand correctly, but you can either set the rates accordingly, e.g.:

Inputs.rates = '5*Hz + ...'

or include it directly into the group definition:

baseline_rate = 5*Hz
Inputs = NeuronGroup(N_Poisson,'''rates : Hz
                                  x_distant : 1 (constant)
                                  y_distant : 1 (constant)''',
                     threshold='rand()<(rates + baseline_rate)*dt')
# or simpler but less flexible:
Inputs = NeuronGroup(N_Poisson,'''rates : Hz
                                  x_distant : 1 (constant)
                                  y_distant : 1 (constant)''',
                     threshold='rand()<(rates + 5*Hz)*dt')