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 (Devices) 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, weave 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 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.
As in the case of runtime code generation, computational work is done by a collection of CodeObject s. In CPPtandaloneDevice, each code object is converted into a pair of .cpp and .h files, and this is probably a fairly typical way to do it. For this device, it just uses the same code generation routines as for the runtime C++ device weave.
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.
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.