SpiNNakerManchester / vor_cerebellum

Cerebellum learning to perform the VOR.
GNU General Public License v3.0
0 stars 0 forks source link

Ensure algorithm feasibility #7

Open ChiaraBartolozzi opened 6 years ago

ChiaraBartolozzi commented 6 years ago

will it work with pre-driven plasticity, can we handle delays correctly, will it be efficient?

ChiaraBartolozzi commented 6 years ago

learning rule for PF-PC

Parallel Fibres (PF that are the output axons of Granular Cells) project to Purkinje Cells

The weight of the connection between PF and PC is plastic and depends on the erro signal carried by the Climbing Fibres (CF)

This is capered by the fact that the learning is triggered by the CF spike, then we need to look back in time the spikes produced by the PF input and use the kernel exp(dt/tau)sin(dt/tau)^20

In SpiNNaker, we need to trigger the learning at occurrence of PF spike (then we know which PF fibre to change).

  1. apply LTP rule, increase the weight by alpha (constant)
  2. if there is a CF spike between the current PF spike and the previous PF spike, then apply the LTD looking at the PF spikes before the CF spike (using the kernel)

So we need to store the PF and CF spikes

  1. PF spikes: for not longer than 200ms before the last CF spike (on the SDRAM)
  2. CF spikes: just the last one on the DTMC and that can be erased when it is used for the LTD for the first time
ChiaraBartolozzi commented 6 years ago

Every time that a CF spike happens, you should calculate the LTD amount corresponding to ALL the previous PF spikes, and not only the last one. There are two ways to implement this:

  1. By storing a buffer with all the PF spikes (or at least the most recent ones) and query the LUT for the f(t)=exp(-t/tau)sin(t/tau)^20 for every combination PF spike/CF spike

  2. store a "learning trace" for each PF synapse. In this second case, every PF spike will add f(0) to its learning trace, and every CF spike will update the learning trace by calculating f(t+delta_t) from f(t), and it will apply the corresponding LTD to each PF synapse. We implement this second method as it tends to be more efficient in terms of simulation time and memory consumption. Indeed, f(t) is conveniently chose to allow this kind of f(t+delta_t) update. But you probably don't need to implement this way in SpiNNaker.

The development of the f(t+delta_t) function is not so straightforward and requires some mathematical development. exp(t+delta_t)=exp(t)exp(delta_t)=f(t)exp(delta_t), that is easy. But sin(t+delta_t)^20 development bases on sin properties, concretely, sin(t+delta_t) = sin(t)cos(delta_t) + cos(t)sin(deltat). The exponential part (20) is then calculated based on the terms extracted in the STDPSinConnection::terms table, and the TermPointer variable points to the terms line corresponding to the 20 exponent. state_vars stores the state variables required for the development and update of the exp(t) and sin(t)^20. I am unsure if we still have the development of the sin(t)^20 part, but if you need it, let me know and we can try to reproduce it.

ChiaraBartolozzi commented 6 years ago

there are two ways for implementing the STDP kernel: a)The buffer-like version; the PF activity is stored in a buffer (obviously since we have a limited storage capacity the buffer length is finite. .A common choice with regards the “eligibly trace” for the buffer length is something about four times the sensorimotor pathway delay (100 ms,). All those PF spikes stored in this buffer are then convolved with the kernel(stored in the LUT (f(t)=exp(-t/tau)sin(t/tau)^20) for every combination of PF spike-CF spike. This version is not developed in NEST but it is in EDLUT. It is easier to understand and implement but it has a major drawback, the memory consumption. If memory resources are not limited, it is then the option to be picked. (please note that tau in f(t)=exp(-t/tau)sin(t/tau)^20 stands for a corrective factor needed to normalise the kernel height , it has nothing to do with the sensorimotor pathway delay)

b) the second option is to implement a mathematical development. Due to the trigonometric properties inherent to sine and cosine functions, we can store in only one variable the “elegibly trace” of our PFs with no temporal or storage restrictions: Please check the development of the sin^2 function

Note that we could just accumulate the past activity in a bunch of variables type FLOAT

This is a simplified development for f(x) =sin(t)^2, this must be expanded to sin(t)^20 in a similar manner which it seems to be way more tedious. If you finally decide to go for this second option, my recommendation is to take the mechanism already developed in NEST as it is.

ChiaraBartolozzi commented 6 years ago

image002

ChiaraBartolozzi commented 6 years ago

Oliver and I decided to implement the STDP rule by storing the PF spike train in the SDRAM. It seems more feasible than storing the LUTs (1024x1024 + 1024x256) in the local memory.

ChiaraBartolozzi commented 6 years ago

For each PF spike, we retrieve the CF spike times (that happened before the current and previous PF spikes)

For each CF spike in the list, then, we take the PF spikes that happened before the CF spike, in a window of 255ms

We retrieve the dt between CF and previous PF spikes and apply the synaptic kernel from the LUT. The weight of the synapse is then depressed multiplicatively for each spike.

For each PF we also potentiate the synapse (additive)

ChiaraBartolozzi commented 6 years ago

When we have the CF spike and the list of previous PF spikes we need to sum the effects of each "depression": sum for i=1:n of dwi*w(i-1)

Which is the correct order? shall we start from the most recent PF event or from the farthest in time?

We decided to start from the oldest one (as the formula is an integral over time from -inf to +inf)

ChiaraBartolozzi commented 6 years ago

TODO:

@oliverrhodes, @ChiaraBartolozzi

@oliverrhodes

ChiaraBartolozzi commented 6 years ago

shall we create one issue for each todo item?