Neurons and Connections
Importing PyNN
The simulator used by a PyNN script is determined by which module is imported from the PyNN package, e.g.:
>>> from pyNN.neuron import * >>> from pyNN.nest1 import * >>> from pyNN.nest2 import * >>> from pyNN.pcsim import *
After this line, all PyNN code is independent of the simulator used, although it is possible to include simulator-specific code in the script as well (if simulator-independence is not important to you, or if you are in the process of porting simulator-specific code to pure PyNN code).
Initialising the simulator
Before using any other functions or classes from PyNN, the user must call the setup() function:
>>> setup()
setup() takes various optional arguments: setting the simulation timestep (there is currently no support in the API for variable timestep methods although native simulator code can be used to select this option where the simulator supports it), setting the minimum and maximum synaptic delays, and turning debugging output on and off, e.g.:
>>> setup(timestep=0.1, min_delay=0.1, max_delay=0.5, debug=False)
Debugging information is written to a log file in the working directory.
Creating neurons
Neurons are created with the create() function. To create a single integrate-and-fire neuron, type:
>>> create(IF_curr_alpha)
Here, IF_curr_alpha is a particular class of IF neuron with alpha-function shaped synaptic currents, that will work with any PyNN simulation engine, whether NEURON, NEST or PCSIM. IF_curr_alpha is a so-called 'standard cell', implemented as a Python class. For more information, see the section StandardCells.
You don't have to use standard cells. You can also use any neuron model that is available in an individual simulator, although of course your simulation will then only run with that simulator, for example:
>>> create('iaf_neuron')
iaf_neuron is a neuron model available in the NEST simulator. Note that the model name has to be given as a string for simulator-specific models.
To create many neurons at once, add the n argument, e.g.:
>>> create(IF_curr_alpha, n=10)
The neurons we have created so far have all had default parameter values, stored in the default_values of the standard cell class, e.g.:
>>> IF_curr_alpha.default_parameters
{'tau_refrac': 0.0, 'tau_m': 20.0, 'i_offset': 0.0, 'cm': 1.0, 'v_init': -65.0,
'v_thresh': -50.0, 'tau_syn_E': 0.5, 'v_rest': -65.0, 'tau_syn_I': 0.5,
'v_reset': -65.0}
To use different parameter values, use the param_dict argument, e.g.:
>>> create(IF_curr_alpha, param_dict={'tau_m': 15.0, 'cm': 0.9}, n=10)
If you try to set a non-existent parameter, or pass an invalid value, PyNN will raise an Exception, e.g.:
>>> create(IF_curr_alpha, param_dict={'foo': 15.0})
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "/usr/lib/python/site-packages/pyNN/nest1.py", line 228, in create
celltype = cellclass(param_dict)
File "/usr/lib/python/site-packages/pyNN/nest1.py", line 68, in __init__
common.IF_curr_alpha.__init__(self,parameters) # checks supplied parameters and adds default
File "/usr/lib/python/site-packages/pyNN/common.py", line 113, in __init__
self.parameters = self.checkParameters(parameters, with_defaults=True)
File "/usr/lib/python/site-packages/pyNN/common.py", line 99, in checkParameters
raise NonExistentParameterError(k)
NonExistentParameterError: foo
>>> create(IF_curr_alpha, param_dict={'tau_m': 'bar'})
Traceback (most recent call last):
File "/usr/lib/python/site-packages/pyNN/nest1.py", line 228, in create
celltype = cellclass(param_dict)
File "/usr/lib/python/site-packages/pyNN/nest1.py", line 68, in __init__
common.IF_curr_alpha.__init__(self,parameters) # checks supplied parameters and adds default
File "/usr/lib/python/site-packages/pyNN/common.py", line 113, in __init__
self.parameters = self.checkParameters(parameters, with_defaults=True)
File "/usr/lib/python/site-packages/pyNN/common.py", line 90, in checkParameters
raise InvalidParameterValueError, (type(supplied_parameters[k]), type(default_parameters[k]))
InvalidParameterValueError: (<type 'str'>, <type 'float'>)
If you wish to do something with the cell after creating it: record from it, change a parameter, connect it to another cell, you should assign the return value of the function to a variable, e.g.:
>>> cell = create(IF_curr_alpha) >>> cell_list = create(IF_curr_alpha, n=10)
The create() function returns either a cell id object or a list of id objects.
Connecting neurons
Any neuron that emits spikes can be connected to any neuron with at least one synapse using the connect() function, e.g.:
>>> spike_source = create(SpikeSourceArray, param_dict={'spike_times': [10.0, 20.0, 30.0]})
>>> cell_list2 = create(IF_curr_exp, n=10)
>>> connect(spike_source, cell_list2)
In this case we connect a spike-generating mechanism (SpikeSourceArray is a 'standard cell' model that emits spikes at times specified by the spike_times parameter) to each cell in the list cells, i.e. we create 10 connections at once. For clarity, we could also have specified the argument names:
>>> connect(source=spike_source, target=cell_list2)
Either source or target or both may be individual cell ids or lists of ids. In the latter case, each source (presynaptic) cell is connected to every target (postsynaptic) cell with probability given by the optional argument p, which defaults to 1, e.g.:
>>> source_list = cell_list >>> target_list = cell_list2 >>> connect(source_list, target_list, p=0.5)
When specifying connections as above, default values are given to the synaptic weight and delay. These values are seldom very useful, and it is better to specify the weight and delay arguments of connect(), e.g.:
>>> connect(source_list, target_list, weight=1.5, delay=0.5)
Weights are specified in nA for 'current-based' synapses or µS for 'conductance-based' synapses. Delays are in ms. For current-based synapses, weights should be negative for inhibitory synapses. For conductance-based synapses, weights should always be positive, since the effect of a synapse is determined by its reversal potential.
If the neuron model has more than one synapse mechanism, or more than one synaptic location, the particular synapse to which the connection should be made is specified with the synapse_type argument, e.g.:
>>> connect(source_list, target_list, weight=1.5, delay=0.5, synapse_type='inhibitory')
(the attribute synapse_types of all standard cell objects contains a list of the synapse types for that cell type).
Setting neuron parameters
There are many ways to change the parameters for individual neurons and post-synaptic mechanisms after creation of the neuron. To change a single parameter of a single neuron, just set the relevant attribute of the neuron ID object, e.g.:
>>> cells = create(IF_curr_exp, param_dict={'v_init': -70.0}, n=10)
>>> cells[0].tau_m
20.0
>>> cells[0].tau_m = 15
>>> cells[0].tau_m
15.0
To change several parameters at once for a single neuron, use the set_parameters() method of the neuron ID, e.g.:
>>> cells[1].set_parameters(tau_m=10.0, cm=0.5) >>> cells[1].tau_m 10.0 >>> cells[1].cm 0.5
To change parameters for several cells at once, use the set() function, e.g.:
>>> set(cells[0:5], param='v_init', val=-65.0) >>> print cells[0].v_init -65.0 >>> print cells[5].v_init -70.0
Individual parameters can be set using the param and val arguments, as above, or multiple parameters can be set at once by passing a dictionary of name:value pairs as the param argument, with val empty, e.g.:
>>> set(cells, param={'tau_refrac': 2.0, 'tau_syn_E': 5.0})
Setting position in space
In some cases it is important to know the position of a neuron in space. This information can be set and retrieved using the position attribute of the neuron ID:
>>> cells[0].position = (75, 456, 56) >>> cells[0].position (75, 456, 56)
Positions must always be in 3D, and may be given as integers or floating-point values, and as tuples or as numpy arrays. No specific scale of units is assumed, although many parts of PyNN do assume a Euclidean coordinate system.
Recording spikes and membrane potential
To record action potentials use the record() function and to record membrane potential use the record_v() function. The arguments for both functions are a cell id or list of ids, and a filename, e.g.:
>>> record(cell, "spikes.dat") >>> record_v(cell_list, "allspikes.dat")
By default, all simulators write data files in the same format.
The beginning of a typical spike file looks like:
# dt = 0.1 # n = 1000 0.0 2 0.3 5 0.4 3 0.9 2 1.0 1 . . .
The beginning of a typical membrane potential file looks like:
# dt = 0.1 # n = 1000 -65.0 0 -64.9 0 -64.7 0 -64.5 0 . . .
Both file types begin with header lines giving the timestep (there is currently no support for variable-time step recording) and the number of data points in the file. Each line of the spike file then gives the occurence time of a spike (in ms) and the id of the neuron in which it was recorded. Each line of the membrane potential file gives the membrane potential (in mV) followed by the id of the neuron in which it was recorded. In both cases, whether the file is sorted by cell id or by time depends on the simulator: it is not standardised.
In some cases it is more efficient to write files in the simulator's native format, rather than the standard PyNN format. In this case, use the compatible_output=False argument to the end() function.
Considerable enhancements to file formats are planned for future releases of PyNN, including recording to binary (HDF5) rather than text files, support for variable time-step recording, and user-specified output formats.
Running a simulation
The run() function runs the simulation for a given number of milliseconds, e.g.:
>>> run(1000.0)
Finishing up
Just as a simulation must be begun with a call to setup(), it must be ended with a call to end().
Examples
There are several example scripts in the test directory of the source distribution.
