brian2-loihi 0.5.2
A Loihi emulator based on Brian2
Requires Python: >3.6

# A Loihi emulator based on Brian2

The package extends *Brian2* classes such that they match Loihi simulations. While the neuron and synapse model results in an exact match to *Loihi*, the pre- and post-synaptic traces have very small variations from the *Loihi* chip due to stochastic rounding.

Further details are explained in a paper coming soon.

## Installation

```
pip install brian2-loihi
```

### Requirements and dependencies

**Python 3.6** or higher is required.

Dependencies are automatically installed by the pip package manager.

If the source code is used directly, the following packages need to be installed:

* [Brian2 (2.4.2 or higher)](https://brian2.readthedocs.io/en/stable/)
* [Numpy](https://numpy.org/)

## Usage

Five *Brian2* classes are extended. Available parameters are reported below. Further down you find example code.

**Note**:

* It is important to use a `LoihiNetwork`. The *Brian2* magic network approach is not supported.
* Don't change the `defaultclock.dt` value. It is set to match *Loihi*.
* Don't reorder the network simulation `schedule`.

First import the package as:

```
from brian2_loihi import *
```

The following classes can be used:

### LoihiNetwork

Extends the `Network` class from *Brian2* and supports the same parameters.

### LoihiNeuronGroup

Extends the `NeuronGroup` class from *Brian2* and supports the following parameters:

* **N** (int): Number of neurons in the group.
* **refractory** (int, 1...64, optional): The refactory period of the neuron.
* **threshold_v_mant** (int, 0...131071, optional): The mantissa of the membrane voltage threshold.
* **decay_v** (int, 0...4096, optional): The membrane voltage decay (note that tau_v = 4096/decay_v)
* **decay_I** (int, 0...4096, optional): The current decay (note that tau_I = 4096/decay_I)
* **name** (str, optional): A unique name for the group, otherwise use `loihi_neurongroup_0`, etc.

### LoihiSynapses

Extends the `Synapses` class from *Brian2* and supports the following parameters:

* **source** (`SpikeSource`): The source of spikes, e.g. a NeuronGroup.
* **target** (`Group`, optional): The target of the spikes, typically a NeuronGroup. If none is given, the same as source().
* **delay** (int, optional): The synaptic delay.
* **dw** (str, optional): Learning rule, using the pre- and post-synaptic traces. Also constant values are allowed. Note that only `*`, `-` and `+` is allowed.
* **w_exp** (int, optional): Weight exponent which scales the weights by 2^(6 + w_exp). The weight exponent can be between -8 and 7.
* **sign_mode** (int, optional): Defines if the synapses are mixed (1), excitatory (2) or inhibitory (3). Excitatory synapses are default. `synapse_sign_mode` can be used for defining the sign mode.
* **num_weight_bits** (int, optional): Defines the precision of the weight, default is 8 bits. `num_weight_bits` is in a range between 0 and 8.
* **imp_x1** (int, optional): The impulse of the first synaptic pre trace x1. The impulse is between 0 and 127.
* **tau_x1** (int, optional): The time constant of the first synaptic pre trace x1. Tau has to be greater or equal to 0.
* **imp_x2** (int, optional): The impulse of the first synaptic pre trace x2. The impulse is between 0 and 127.
* **tau_x2** (int, optional): The time constant of the first synaptic pre trace x2. Tau has to be greater or equal to 0.
* **imp_y1** (int, optional): The impulse of the first synaptic post trace y1. The impulse is between 0 and 127.
* **tau_y1** (int, optional): The time constant of the first synaptic pre trace y1. Tau has to be greater or equal to 0.
* **imp_y2** (int, optional): The impulse of the first synaptic post trace y2. The impulse is between 0 and 127.
* **tau_y2** (int, optional): The time constant of the first synaptic pre trace y2. Tau has to be greater or equal to 0.
* **imp_y3** (int, optional): The impulse of the first synaptic post trace y3. The impulse is between 0 and 127.
* **tau_y3** (int, optional): The time constant of the first synaptic pre trace y3. Tau has to be greater or equal to 0.
* **name** (str, optional):  The name for this object. If none is given, a unique name of the form. `loihi_synapses`, `loihi_synapses_1`, etc. will be automatically chosen.

### LoihiStateMonitor

Extends the `StateMonitor` class from *Brian2* and supports the following parameters:

* **source** (`Group`): Which object to record values from.
* **variable** (str): Which variables to record, check the `state` object for details.
* **record** (bool, sequence of ints): Which indices to record, nothing is recorded for ``False``, everything is recorded for ``True`` (warning: may use a great deal of memory), or a specified subset of indices.
* **order** (int, optional): The priority of of this group for operations occurring at the same time step and in the same scheduling slot. Defaults to 0.
* **name** (str, optional): A unique name for the object, otherwise will use `source.name+'loihi_statemonitor_0'`, etc.

### LoihiSpikeMonitor

Extends the `SpikeMonitor` class from *Brian2* and supports the following parameters:

* **source** (`Group`): Which object to record values from.
* **variable** (str, optional): Which variables to record at the time of the spike (in addition to the index of the neuron). Can be the name of a variable or a list of names
* **record** (bool, sequence of ints, optional): Which indices to record, nothing is recorded for ``False``, everything is recorded for ``True`` (warning: may use a great deal of memory), or a specified subset of indices.
* **order** (int, optional): The priority of of this group for operations occurring at the same time step and in the same scheduling slot. Defaults to 0.
* **name** (str, optional): A unique name for the object, otherwise will use `source.name+'_loihi_spikemonitor_0'`, etc.

### LoihiSpikeGeneratorGroup

Extends the `SpikeGeneratorGroup` class from *Brian2* and supports the following parameters:

* **N** (int): The number of "neurons" in this group
* **indices** (array of integers): The indices of the spiking cells
* **times** (list (int)): The spike times for the cells given in ``indices``. Has to have the same length as ``indices`` and has to be integer (without time units)
* **period** (int, optional): If this is specified, it will repeat spikes with this period. A period of 0 means not repeating spikes.
* **order** (int, optional): The priority of of this group for operations occurring at the same time step and in the same scheduling slot. Defaults to 0.
* **sorted** (bool, optional):  Whether the given indices and times are already sorted. Set to ``True`` if your events are already sorted (first by spike time, then by index), this can save significant time at construction if your arrays contain large numbers of spikes. Defaults to ``False``.
* **name** (str, optional): A unique name for the object, otherwise will use `loihi_spikegeneratorgroup_0'`, etc.

## Example

More examples and further details are provided in this repository:

https://github.com/sagacitysite/brian2_loihi_utils

Here we just provide a simple example.

### Single neuron

```
import matplotlib.pyplot as plt
from brian2_loihi import *

# Define a single neuron
loihi_group = LoihiNeuronGroup(
    1,
    refractory=2,
    threshold_v_mant=400,
    decay_v=1024,
    decay_I=1024
)

# Excitatory input spikes
ex_neuron_indices = [0, 0, 0, 0]
ex_spike_times = [12, 14, 40, 80]

# Inhibitory input spikes
in_neuron_indices = [0, 0, 0]
in_spike_times = [50, 60, 90]

# Define spike generators
generator_ex = LoihiSpikeGeneratorGroup(1, ex_neuron_indices, ex_spike_times)
generator_in = LoihiSpikeGeneratorGroup(1, in_neuron_indices, in_spike_times)

# Connect excitatory generator with neuron
syn_ex = LoihiSynapses(generator_ex, loihi_group, sign_mode=synapse_sign_mode.EXCITATORY)
syn_ex.connect()
syn_ex.w = 124

# Connect inhibitory generator with neuron
syn_in = LoihiSynapses(generator_in, loihi_group, sign_mode=synapse_sign_mode.INHIBITORY)
syn_in.connect()
syn_in.w = -124

# Probe synaptic input using a state monitor
mon_I = LoihiStateMonitor(loihi_group, 'I')
# Probe voltage using a state monitor
mon_v = LoihiStateMonitor(loihi_group, 'v')

# NOTE: It is important to use the LoihiNetwork,
#       using Brian's magic network is not provided
net = LoihiNetwork(
    loihi_group,
    generator_in,
    generator_ex,
    syn_ex,
    syn_in,
    mon_I,
    mon_v
)

# Run the simulation
net.run(100, report='text')

# Plot synaptic input (current)
plt.plot(mon_I.I[0])
plt.title('Synaptic input / Current')
pl = plt.show()

# Plot voltage
plt.plot(mon_v.v[0])
plt.title('Voltage')
pl = plt.show()
```

## References

### Emulator

The emulator is described in

... coming soon ...

### Loihi

The Loihi chip was developed by Intel and is introduced in

[M. Davies et al., "Loihi: A Neuromorphic Manycore Processor with On-Chip Learning," in IEEE Micro, vol. 38, no. 1, pp. 82-99, January/February 2018, doi: 10.1109/MM.2018.112130359.](https://doi.org/10.1109/MM.2018.112130359)

Some further details are given in

[C. Lin et al., "Programming Spiking Neural Networks on Intel’s Loihi," in Computer, vol. 51, no. 3, pp. 52-61, March 2018, doi: 10.1109/MC.2018.157113521.](https://doi.org/10.1109/MC.2018.157113521)