Access synaptic variable in neuron (intrinsic plasticity)

I’m working on implementing a model where the neuron firing thresholds depend on the value of another variable which is defined in the synapses; basically, it’s a model of intrinsic plasticity.

Is there any way to access and use a synaptic variable defined as:

du/dt = ((U - u)/tau_f) : 1
u = u + U*(1 - u)

in such a way in my neurons:

dVth_e_adapt/dt = 0.6 / (1 + exp(u)) : volt

Hi @Tioz90. In general, synapses can access the variables of the neurons they are connected to, but not the other way round. This is simply because a neuron can be connected to many synapses, so it is not clear what value should be used. The mechanism to get a value from multiple synapses into a neuron at every time step would be a summed variable, but as the name suggests this would take the sum over all values of the variables in the respective synapses. If you only have a single synapse per neuron, the easiest way would be to include the synaptic equations in the neuronal model, and to refer to them when needed (e.g. in on_pre).
Hope that makes sense?

Thanks! As you suggested, I tried moving the du/dt = ((U - u)/tau_f) : 1 to the neuron equations and leaving u = u + U*(1 - u) in the on_pre of the synapse, but I’m getting this error:

File “/Users/thomas/PycharmProjects/wm_colaboration/helper_functions/recurrent_competitive_network.py”, line 662, in set_state_monitors
self.E_E_rec = StateMonitor(source=self.E_E,
File “/Users/thomas/.conda/envs/brian/lib/python3.9/site-packages/brian2/monitors/statemonitor.py”, line 248, in init
self.variables.add_reference(f’source{varname}',
File “/Users/thomas/.conda/envs/brian/lib/python3.9/site-packages/brian2/core/variables.py”, line 1821, in add_reference
raise TypeError(f"Cannot link variable ‘{name}’ to ‘{varname}’ in "
TypeError: Cannot link variable ‘_source_u’ to ‘u’ in group ‘E_E’ – need to precalculate direct indices but index _postsynaptic_idx can change

I think you might be trying to record the variable u with a StateMonitor on the Synapses object instead of on the NeuonrGroup object – if you move the equation you should change the group to record from as well? If that is not the case, could you please share some minimal code that leads to this error?

I’ve moved the differential equation to the neuron and left the even-driven update in the synapse, but now I seem to have two different independent u variables. In fact, I can record both and they have different values.
Should I move all equations to the neuron and then simply access u from the synapse?

With the event-driven update you mean the on_pre statement? This has to stay in the synapse, since it has to be triggered by incoming spikes. But I don’t see how you could have two u variables if the Synapses object does not define a u variable in its equations. Can you show the definitions of your Synapses and NeuronGroup objects?

By using the debugger, these are the equations at runtime in the NeuronGroup object:

dVepsp/dt = -Vepsp / (3.5 * msecond) : V
dVipsp/dt = -Vipsp / (5.5 * msecond) : V
dVm/dt = int(not_refractory)*((Vepsp - Vipsp - (Vm - (-65. * mvolt))) / (20. * msecond)) : V (unless refractory)
dVth_e/dt = ((-52. * mvolt) - Vth_e) / (1.8 * second) : V
du/dt = ((U - u) / tau_f) : 1
lastspike : s
not_refractory : 1

and these in the Synapses:

dx_/dt = ((1 - x_)/tau_d)*int(plastic_2) : 1 (clock-driven)
plastic : 1 (shared)
plastic_2 : 1 (shared)
w : V

In the code this is the Synapses model definition:

w : volt
plastic : boolean (shared)
plastic_2 : boolean (shared)
dx_/dt = ((1 - x_)/tau_d)*int(plastic_2) : 1 (clock-driven)

, the on_pre :

u_update: u = u + U * (1 - u) * int(plastic_2)
x_update: x_ = x_ - u * x_ * int(plastic_2)
Vepsp_transmission: Vepsp += w * ((x_ * u)/U)

While this is the NeuronGroup model definition:

dVepsp/dt = -Vepsp / (3.5 * msecond) : V
dVipsp/dt = -Vipsp / (5.5 * msecond) : V
dVm/dt = (Vepsp - Vipsp - (Vm - (-65. * mvolt))) / (20. * msecond) : V (unless refractory)
dVth_e/dt = ((-52. * mvolt) - Vth_e) / (1.8 * second) : V
du/dt = ((U - u) / tau_f) : 1

Could the problem be the assignment operator when I use u in the on_pre :
u = u + U * (1 - u) * int(plastic_2)
?

This looks ok to me. Are you sure that there is actually a problem? In the Synapses, since you do not specify any variable u, the name u is a shorthand for u_post, i.e. the variable u in the target group. You can record it, but for each synapse it should give you the values of the target group.
Here’s a simple example demonstrating this:

G = NeuronGroup(5, 'dv/dt = -v/(10*ms) : 1')
G.v = 'i*0.1 + 0.1'
S = Synapses(G, G)
S.connect(i=[0, 1, 2], j=[2, 3, 3])
G_mon = StateMonitor(G, 'v', record=True)
S_mon = StateMonitor(S, 'v', record=True)
run(1*ms)
print('Neuron recordings')
print(G_mon.v[:])
print('Synapse recordings for', S.j[:])
print(S_mon.v[:])

The StateMonitor on S records v_post for every synapse. This leads to this output:

Neuron recordings
[[0.1        0.09900498 0.09801987 0.09704455 0.09607894 0.09512294
  0.09417645 0.09323938 0.09231163 0.09139312]
 [0.2        0.19800997 0.19603973 0.19408911 0.19215789 0.19024588
  0.18835291 0.18647876 0.18462327 0.18278624]
 [0.3        0.29701495 0.2940596  0.29113366 0.28823683 0.28536883
  0.28252936 0.27971815 0.2769349  0.27417936]
 [0.4        0.39601993 0.39207947 0.38817821 0.38431578 0.38049177
  0.37670581 0.37295753 0.36924654 0.36557247]
 [0.5        0.49502492 0.49009934 0.48522277 0.48039472 0.47561471
  0.47088227 0.46619691 0.46155817 0.45696559]]
Synapse recordings for [2 3 3]
[[0.3        0.29701495 0.2940596  0.29113366 0.28823683 0.28536883
  0.28252936 0.27971815 0.2769349  0.27417936]
 [0.4        0.39601993 0.39207947 0.38817821 0.38431578 0.38049177
  0.37670581 0.37295753 0.36924654 0.36557247]
 [0.4        0.39601993 0.39207947 0.38817821 0.38431578 0.38049177
  0.37670581 0.37295753 0.36924654 0.36557247]]

You can see that the values recorded for the first synapse match the values of the neuron with index 2, and the following two synapses both match the values of the neuron with index 3, since both synapses connect to the same neuron.

Ah, I see what you mean, so I guess that then I should be specifying I want to use u_pre in the Synapses ?

The fact is that I would like u to be a pre-synaptic mechanism which works this way: when a pre-synaptic neuron spikes then u for that neuron is increased and u is always decaying back to its baseline value U.
I would then like to use u both to set the neuron threshold Vth_e but also the synaptic weights Vepsp.

Is my current setup capable of achieving this or am I getting confused somewhere?

Ah, I see, that’s a bit different from what I thought. In that case, you can actually move the on_pre part which updates u into the neuron, in contrast to what I said before – it becomes part of the reset. This is actually important in this case, since otherwise u would be updated several times if a neuron spikes once but connects to multiple target neurons… And then when you use it to modulate the synaptic transmission, you’d indeed refer to u_pre to refer to the u value of the pre-synaptic neuron.

Perfect, it’s working now! Thank you so much! :smile:

1 Like