|Welcome to The Neuromorphic Engineer|
|A Python interface to NEST|
PDF version | Permalink
NEST1 is a simulator for large networks of spiking neurons, and has a long history of use in computational neuroscience for the simulation of large spiking networks. It runs on many different architectures (ranging from normal desktop computers to computer clusters with thousands of processor cores2), is written in C++, and has a built-in simulation language interpreter (SLI) to help the user set up the network. NEST's simulation language is stack-based and inspired by PostScript,3 which means that each function expects its arguments to be on the stack and returns results back to it. SLI is a high-level language with functional operators like Map and data-structures like associative arrays. However, learning SLI has turned out to be difficult for many users: a more convenient simulation language was required.
When we were thinking about a new scripting interface for NEST, Python was almost unknown in computational neuroscience. However, we noticed a strong trend towards it in the scientific community in general.4 Python has a number of advantages over commercial programming environments like Matlab5 or Mathematica:6 it is installed on almost all Linux and MacOS-based computers, is free, and is being developed by an active community. Thanks to the multitude of packages for scientific computing (http://www.scipy.org), Python can be used for stimulus generation, data analysis, and plotting in the same way its commercial alternatives can. As a result, a number of other neuroscience laboratories are also using Python.7
The usual approach to creating Python interfaces for existing software is to create a wrapper library that exposes all data structures and functions of the application to Python. We decided to deviate from this approach and keep the existing SLI interface as an intermediate layer between the new user interface and the simulation engine. The reason for this is threefold: first, a lot of SLI code has already been written, and we did not want to render this code useless with newer versions of NEST. Second, Python is not yet available on some exotic hardware platforms, which we still need to use. Third, NEST needs to remain independent of third party software to guarantee long-term sustainability.
As a result, the PyNEST interface consists of two separate layers: the low-level API, written using the Python C API,8 is responsible for the data conversion from SLI to Python and back. It provides access to SLI with only three functions: sli_push() for pushing data onto the operand stack, sli_pop() for getting data from the SLI stack back to Python, and sli_run() for executing SLI commands. The high-level API uses these functions of the low-level API to provide Python versions of all important SLI commands. The functions of the high-level API are used by the user's simulation code. The complete architecture of PyNEST is shown in Figure 1.
SLI already provides all the necessary commands to build and simulate a neural network. Thus, we can create a complete Python interface to NEST by creating wrapper functions that just call the respective functions in SLI. This technique is illustrated in the following listing, which defines a function that returns the list of available models:
In general, each function has three parts: First, we push the arguments onto the SLI stack with sli_push(). Second, we execute one or more SLI commands to perform the desired action inside of NEST using sli_run(). Third, we retrieve the results from the stack via sli_pop(). Note that the example above does not contain a call to sli_push(), as no arguments are required. The low-level API catches all errors of NEST and raises an appropriate Python exception.
The function sli_push() has to convert a given Python object to the corresponding SLI data type. We first determine the type of the Python object and then instantiate a new SLI datum of the right type. sli_pop() converts a SLI datum to a Python object. This conversion has a more elegant implementation, because it can exploit the fact that each SLI datum knows its own type. A SLI datum thus can convert itself to a Python object of the right type. To avoid that SLI datums directly depend on Python, we use the acyclic visitor pattern,9 which moves Python dependent code to a separate class. The details of this technique are explained in Eppler et al. 2008.10
We have shown an alternative approach for creating Python bindings for an application by using a generic interpreter-interpreter interaction instead of direct wrapping of the underlying functions and data structures. The implementation of PyNEST is described in detail together with examples and the complete API reference in Eppler et al. 2008.10 NEST's source code is available under an open-source license for non-commercial use on the homepage of the NEST Initiative at http://nest-initiative.org. Currently, we are investigating methods for writing neuron and synapse models in Python instead of C++ in order to ease the development for users not familiar with the internal workings of NEST. In another project, we are improving the scalability of NEST for very large clusters with tens of thousands of processors, e.g. the IBM BlueGene architecture.
Tell us what to cover!
If you'd like to write an article or know of someone else who is doing relevant and interesting stuff, let us know. E-mail the editor and suggest the subject for the article and, if you're suggesting someone else's work, tell us their name, affiliation, and e-mail.