Devices
This document describes how to implement a new Device
for Brian. This is a
somewhat complicated process, and you should first be familiar with devices
from the user point of view (Computational methods and efficiency) as well as the code
generation system (Code generation).
We wrote Brian’s devices system to allow for two major use cases, although it can potentially be extended beyond this. The two use cases are:
Runtime mode. In this mode, everything is managed by Python, including memory management (using numpy by default) and running the simulation. Actual computational work can be carried out in several different ways, including numpy or Cython.
Standalone mode. In this mode, running a Brian script leads to generating an entire source code project tree which can be compiled and run independently of Brian or Python.
Runtime mode is handled by RuntimeDevice
and is already implemented, so here
I will mainly discuss standalone devices. A good way to understand these
devices is to look at the implementation of CPPStandaloneDevice
(the only
one implemented in the core of Brian). In many cases, the simplest way to
implement a new standalone device would be to derive a class from
CPPStandaloneDevice
and overwrite just a few methods.
Memory management
Memory is managed primarily via the Device.add_array
, Device.get_value
and
Device.set_value
methods. When a new array is created, the add_array
method is called, and when trying to access this memory the other two are
called. The RuntimeDevice
uses numpy to manage the memory and returns the
underlying arrays in these methods. The CPPStandaloneDevice
just stores
a dictionary of array names but doesn’t allocate any memory. This information
is later used to generate code that will allocate the memory, etc.
Code objects
As in the case of runtime code generation, computational work is done by
a collection of CodeObject
s. In CPPStandaloneDevice
, each code object
is converted into a pair of .cpp
and .h
files, and this is probably
a fairly typical way to do it.
Building
The method Device.build
is used to generate the project. This can be
implemented any way you like, although looking at CPPStandaloneDevice.build
is probably a good way to get an idea of how to do it.
Device override methods
Several functions and methods in Brian are decorated with the device_override
decorator. This mechanism allows a standalone device to override the behaviour
of any of these functions by implementing a method with the name provided to
device_override
. For example, the CPPStandaloneDevice
uses this to
override Network.run
as CPPStandaloneDevice.network_run
.
Other methods
There are some other methods to implement, including initialising arrays, creating spike queues for synaptic propagation. Take a look at the source code for these.