1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
|
====================
Adding a new backend
====================
Structure of the codebase
=========================
PyNN is both an API for simulator-independent model descriptions and an
implementation of that API for a number of simulators.
If you wish to add PyNN support for your own simulator, you are welcome to add
it as part of the main PyNN codebase, or you can maintain it separately. The
advantage of the former is that we can help maintain it, and keep it up to date
as the API evolves.
A PyNN-compliant interface is not required to use any of the code from the
``pyNN`` package, it can implement the API entirely independently. However, by
basing an interface on the "common" implementation you can save yourself a lot
of work, since once you implement a small number of low-level functions and
classes, you get the rest of the API for free.
The common implementation
-------------------------
Recording
~~~~~~~~~
The ``recording`` modules provides a base class ``Recorder`` that exposes
methods ``record()``, ``get()``, ``write()`` and ``count()``. Each simulator
using the common implementation then subclasses this base class, and must
implement at least the methods ``_record()``, ``_get()`` and ``_local_count()``.
Each ``Recorder`` instance records only a single variable, whose name is passed
in the constructor.
By default, PyNN scales recorded data to the standard PyNN units (mV for voltage,
etc.), reorders columns if necessary, and adds initial values to the beginning
of the recording if the simulator does not record the value at time 0. In this
way, the structure of the output data is harmonized between simulators. For
large datasets, this can be very time-consuming, and so this restructuring can
be turned off by setting the ``compatible_output`` flag to ``False``.
.. TODO: discuss output file formats.
.. TODO: discuss gathering with MPI
The NEST interface
------------------
The NEURON interface
--------------------
The Brian interface
-------------------
Adding a new simulator interface
================================
The quickest way to add an interface for a new simulator is to implement the
"internal API", described below. Each simulator interface is implemented as
a sub-package within the ``pyNN`` package. The suggested layout for this
sub-package is as follows::
|\_ __init__.py
|\_ cells.py
|\_ connectors.py
|\_ electrodes.py
|\_ recording.py
|\_ simulator.py
\_ synapses.py
The only two files that are *required* are ``__init__.py`` and ``simulator.py``:
the contents of all the other modules being imported into ``__init__.py``.
[Maybe just provide a template, rather than discussing the whole thing]
__init__:
list_standard_models() [surely this could be in common?]
setup() - should call common.setup() and then do whatever initialization is necessary for your backend
end() - should be in common
run = common.build_run(simulator)
reset = common.build_reset(simulator)
initialize = common.initialize
get_current_time, get_time_step, get_min_delay, get_max_delay, \
num_processes, rank = common.build_state_queries(simulator)
create = common.build_create(Population)
connect = common.build_connect(Projection, FixedProbabilityConnector)
set = common.set ??
record = common.build_record(simulator)
record_v = lambda source, filename: record(['v'], source, filename)
record_gsyn = lambda source, filename: record(['gsyn_exc', 'gsyn_inh'], source, filename)
simulator:
class State
run
clear
reset
properties: t, dt, ...
standardmodels
populations:
class ID
class Population
_simulator = simulator
_recorder_class = Recorder
_assembly_class = Assembly
_create_cells
_set_initial_value_array
_get_view
_get_parameters
_set_parameters
class PopulationView
_assembly_class = Assembly
_simulator = simulator
_set_initial_value_array
_get_view
_get_parameters
_set_parameters
class Assembly
_simulator = simulator
projections
class Connection
class Projection
_simulator = simulator
__init__
__len__
set
_convergent_connect
recording
class Recorder(recording.Recorder):
_simulator = simulator
_record
_get_spiketimes
_get_all_signals
(staticmethod) find_units
_local_count
A walk through the lifecycle of a simulation
============================================
Import phase
------------
[What happens on import]
Setup phase
-----------
[What happens when calling setup()]
Creating neurons
----------------
On creating a Population...
- create default structure, if none specified
- create StandardCellType instance (if using standard cells)
- check and translate parameters, translated parameters stored in parameters attribute
- create recorders
- create neurons, determine local_mask
Finally, we set initial values for all neurons' state variables, e.g. membrane
potential. The user may set these values later with a call to the initialize()
method, but in case they don't we set them here to default values. Defaults are
set on a model-by-model basis: each StandardCellType subclass has a dictionary
attribute called default_initial_values. [For now, these must be numeric values.
It would be nice to allow them to be the names of parameters, allowing the
initial membrane potential to be set to the resting membrane potential, for
example]. This of course causes a problem - not yet resolved - for non
standard cells. These initial values are immediately passed through to the
simulator. We set initial values using the initialize() method, which in turn
updates the initial_values attribute - we do not modify initial_values directly:
probably it should be read-only.
Creating connectors
-------------------
Composing synaptic plasticity models
------------------------------------
Connecting neurons
------------------
Instrumenting the network
-------------------------
Running a simulation
--------------------
Retrieving/saving recorded data
-------------------------------
Finishing up, or resetting for a new run
----------------------------------------
|