# LookUp Table approximator for Brian2

**URL:** <https://brian.discourse.group/t/lookup-table-approximator-for-brian2/478>\
**Category:** Showcase\
**Created:** [19 September 2021 03:09 UTC](https://brian.discourse.group/t/lookup-table-approximator-for-brian2/478 "2021-09-19T03:09:47Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![rth](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/rth/32/16_2.png) [@rth](https://brian.discourse.group/u/rth)\
**Post date:** [19 September 2021 03:09 UTC](https://brian.discourse.group/t/lookup-table-approximator-for-brian2/478/1 "2021-09-19T03:09:47Z")

</div>

Hi All,

I thought about this project for quite a while, but funnaly I implemented the lookup-table approximation in Brian2.

### What is Lookup table approximation?

The lookup-table approximation is a classical method for computation acceleration in a numerical problem, known for centuries. It is extensively used in such software as NEURON and GENESIS, but I kind of miss it in Brian. This approximation is based on a straightforward algorithm: First, before simulation, one needs to precompute lookup tables for values of m\_\infty(v), h\_\infty(v), and so on in a full range of voltages. Usually, this range goes between the lowest possible to the highest possible voltages. The range is divided into intervals with constant steps. For example, this may be a range from −100 to 60 mV with 1 mV step. With precomputed tables, one solves differential equations using linear interpolation between table columns instead of computing exponential functions. The voltage at a current time moment of a numerical solution is used to find indices of two columns in the lookup table closest to the membrane voltage. Using these two indices, one can query values for all steady-states and time constants of gating variables and linearly interpolate between these values - like this:

 ![SP-Figure1](https://global.discourse-cdn.com/free1/uploads/brian/original/1X/f9b998274e22d31bb97cd17aa4ed6598d9b69bfd.jpeg)

### How to use it in Brian2?

You need to install the module `lut4brian` first.

```bash
pip install lut4brian --user

```

In the code one needs to import the module and set up 4 critical module variables:

- lut4brian.Xmin - minimal value of index variable (voltage in the case of ion channels)
- lut4brian.Xmax - maximal value of the same variable
- lut4brian.dX - and the step
- lut4brian.tbl - is a `numpy` array with all your values. Rows are different variables (m\_\infty(v), h\_\infty(v), etc.). Columns are values for the index.

Here is an example of the code:

```python
from brian2 import *
from lut4brian import *

x=linspace(-100,50,151)

lut4brian.Xmin = -100.
lut4brian.Xmax = 50.
lut4brian.dX = 1.

alpha_m = 0.1*10./exprel(-(x+35.)/(10.))
beta_m = 4*exp(-(x+60.)/(18.))
minf = alpha_m/(alpha_m+beta_m)

alpha_h = 0.07*exp(-(x+58.)/(20.))
beta_h = 1./(exp(-0.1*(x+28.))+1.)
htau = 1./(alpha_h+beta_h)
hinf = alpha_h*htau

alpha_n = 0.01*10/exprel(-(x+34.)/(10.))
beta_n = 0.125*exp(-(x+44.)/(80.))
ntau = 1./(alpha_n+beta_n)
ninf = alpha_n*ntau

lut4brian.tbl = array([minf,hinf,htau,ninf,ntau])

```

The table `lut4brian.tbl` has shape, in this case, `(5, 151)` - 5 variables tabled for 151 points each

For `cpp_standalone` and `cython` targets, one _needs to create the table on the level of C-code/Cython_ - simple, just call

```python
lutinit()

```

this will create `lut4brian.c` in your current directory. After that tables can be deleted to release memory.

Now, in equation section one can pool interpolation values right out of the table:

```python
neurons = NeuronGroup(1000, 
'''
dv/dt = (-gNa*minf**3*h*(v-ENa)-gK*n**4*(v-EK)-gL*(v-EL)+I+b*gsyn*(Esyn-v))/Cm : volt
minf = lutinterpol(v/mV,0) : 1
hinf = lutinterpol(v/mV,1) : 1
htau = lutinterpol(v/mV,2) : 1
ninf = lutinterpol(v/mV,3) : 1
ntau = lutinterpol(v/mV,4) : 1
dh/dt = 5*(hinf-h)/htau/ms : 1
dn/dt = 5*(ninf-n)/ntau/ms : 1
db/dt = -b/taus : 1
I : amp
''' , threshold="v>0.*mV", refractory="v>0.*mV", method='euler')

```

The `lutinterpol` function uses voltage to compute index in the table and returns interpolation value. Note we added individual tables in this order `minf`, `hinf`, `htau`, `ninf`, `ntau` and fetched variables by corresponding ‘curve index’ - the second argument of the `lutinterpol()` function.

### Does it work faster than direct computations?

Well, check it out. In test directory on the [GitHub](https://github.com/rat-h/lut4brian) there are a few benchmarks.

| | Original | Lookup table |
| --- | --- | --- |
| Cython | 22 s | 15s |
| CPP standalone (single core) | 53s | 21s |

_I haven’t checked `numpy` implementation yet_

I actually expected 3-4 fold acceleration, but on average, it is only twice faster than simulations with “fully computed” steady-states and taus.

Let me know what do you think.  
RTH

---

<div class="post-metadata">

**Author:** ![mstimberg](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/mstimberg/32/11_2.png) [@mstimberg](https://brian.discourse.group/u/mstimberg)\
**Post date:** [20 September 2021 09:01 UTC](https://brian.discourse.group/t/lookup-table-approximator-for-brian2/478/2 "2021-09-20T09:01:15Z")

</div>

Thanks a lot, looks great! I will have a detailed look later this week.  
We actually had a discussion about this feature in the github tracker, starting about 8 years ago 😱 [Precomputed tables · Issue #87 · brian-team/brian2 · GitHub](https://github.com/brian-team/brian2/issues/87)  
It would be great to have this feature tightly integrated into the standard Brian syntax, but of course having an existing solution like yours beats having a nicely integrated but non-existing one 😉

As a somewhat tangential thought, I wonder whether we should make it possible for external packages to extend Brian’s syntax (e.g. with new flags) and to hook into other places (e.g. the `before_run` method of objects that can be used for initializations).

---

<div class="post-metadata">

**Author:** ![rth](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/rth/32/16_2.png) [@rth](https://brian.discourse.group/u/rth)\
**Post date:** [20 September 2021 16:11 UTC](https://brian.discourse.group/t/lookup-table-approximator-for-brian2/478/3 "2021-09-20T16:11:02Z")

</div>

### Full Disclosure:

- This is a pre-pre-pre-pre-pre-pre-pre-pre-pre-…-alpha version. There may be **lots** of 🐛 inside. 🙄
- The test suit isn’t designed well and needs to be well thought through.
- The code was written overnight after frustration with a pretty big model which needs 30 minutes to compute 1 ms on 32 CPUs computer. Binge-style programming never generates clear code. 🥴
- I posted it here with some hope that someone in the community may be excited about this project and want to contribute to it. Well, I probably should put it in the ‘project’ category instead of ‘showcase’.
- I’m **not** sure (pretty big topic for discussion) that array for lookup table is organized in the most efficient way. There is a lot of literature on setting a 2D-array so the cache controllers can read them efficiently. But because lookup tables generate almost random memory access, I don’t think there is any optimization to help cache controllers.

### Re:

> It would be great to have this feature tightly integrated into the standard Brian syntax

Entirely agree! Also, it seems, the code may be much better optimized than for external module, am I right?

> As a somewhat tangential thought

@mstimberg, it seems a very reasonable idea to have ‘plugin’ interfaces in all Brian’s parts. The problem with such an API is that plugins can fight with each other, and Brian won’t have any control over plugin collisions. It may add unnecessary complexity in the whole system.

---

<div class="post-metadata">

**Author:** ![rth](https://yyz2.discourse-cdn.com/free1/user_avatar/brian.discourse.group/rth/32/16_2.png) [@rth](https://brian.discourse.group/u/rth)\
**Post date:** [20 October 2021 13:21 UTC](https://brian.discourse.group/t/lookup-table-approximator-for-brian2/478/4 "2021-10-20T13:21:18Z")

</div>

#### LUT 1 mV step

 ![LUT-1mV](https://global.discourse-cdn.com/free1/uploads/brian/original/1X/120b373f7ad311f5cbafb362b6e06525643ea3d0.png)

#### LUT 0.5 mV step

 ![LUT-0.5mV](https://global.discourse-cdn.com/free1/uploads/brian/original/1X/4807bc272e12c065ba0b06735c749ebd50e51ee1.png)
