File: parameters.txt

package info (click to toggle)
pynn 0.10.1-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 6,156 kB
  • sloc: python: 25,612; cpp: 320; makefile: 117; sh: 80
file content (225 lines) | stat: -rw-r--r-- 7,437 bytes parent folder | download | duplicates (3)
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
223
224
225
==================
Parameter handling
==================

.. testsetup::

    import numpy
    from lazyarray import larray

.. module:: pyNN.parameters

.. note:: these classes are not part of the PyNN API. They should not be used in
          PyNN scripts, they are intended for implementing backends. You are not
          required to use them when implementing your own backend, however, as
          long as your backend conforms to the API.

The main abstractions in PyNN are the population of neurons, and the set of
connections (a 'projection') between two populations. Setting the parameters of
individual neuron and synapse models, therefore, mainly takes place at the level
of populations and projections.

.. note:: it is also possible to set the parameters of neurons and synapses
          individually, but this is generally less efficient.

Any model parameter in PyNN can be expressed as:

  * a single value - all neurons in a population or synapses in a projection get
    the same value
  * a :class:`RandomDistribution` object - each element gets a value drawn from
    the random distribution
  * a list/array of values of the same size as the population/projection
  * a mapping function, where a mapping function accepts a either a single
    argument ``i`` (for a population) or two arguments ``(i, j)`` (for a
    projection) and returns a single value.

A "single value" is usually a single number, but for some parameters (e.g. for
spike times) it may be a list/array of numbers.

To handle all these possibilities in a uniform way, and at the same time allow
for efficient parallelization, in the 'common' implementation of the PyNN API
all parameter values are converted into :class:`LazyArray` objects, and the
set of parameters for a model is contained in a :class:`dict`-like object,
:class:`ParameterSpace`.


The :class:`LazyArray` class
----------------------------

:class:`LazyArray` is a PyNN-specific sub-class of a more general class,
:class:`larray`, and most of its functionality comes from the parent class. Full
documentation for :class:`larray` is available in the lazyarray_ package, but we
give here a quick overview.

:class:`LazyArray` has three important features in the context of PyNN:

  1. any operations on the array (potentially including array construction) are
     not performed immediately, but are delayed until evaluation is specifically
     requested.
  2. evaluation of only parts of the array is possible, which means that in a
     parallel simulation with MPI, all processes have the same :class:`LazyArray`
     for a parameter, but on a given process, only the part of the array which
     is needed for the neurons/synapses that exist on that process need be
     evaluated.
  3. single often all neurons in a population or synapses in a projection have
     the same value for a given parameter, a :class:`LazyArray` created from a
     single value evaluates to that value: the full array is never created
     unless this is requested.

For example, suppose we have two parameters, `tau_m`, which is constant,
and `v_thresh` which varies according to the position of the neuron in the
population.

.. doctest::

    >>> from pyNN.parameters import LazyArray
    >>> tau_m = 2 * LazyArray(10.0, shape=(20,))
    >>> v_thresh = -55 + LazyArray(lambda i: 0.1*i, shape=(20,))

If we evaluate `tau_m` we get a full, homogeneous array:

.. doctest::

    >>> tau_m.evaluate()
    array([ 20.,  20.,  20.,  20.,  20.,  20.,  20.,  20.,  20.,  20.,  20.,
            20.,  20.,  20.,  20.,  20.,  20.,  20.,  20.,  20.])

but we could also have asked just for the single number, in which case the
full array would never be created:

.. doctest::

    >>> tau_m.evaluate(simplify=True)
    20.0

Similarly, we can evaluate `v_thresh` to get a normal NumPy array:

.. doctest::

    >>> v_thresh.evaluate()
    array([-55. , -54.9, -54.8, -54.7, -54.6, -54.5, -54.4, -54.3, -54.2,
           -54.1, -54. , -53.9, -53.8, -53.7, -53.6, -53.5, -53.4, -53.3,
           -53.2, -53.1])

but we can also take, for example, only every fifth value, in which case the
operation "add -55" only gets performed for those elements.

.. doctest::

    >>> v_thresh[::5]
    array([-55. , -54.5, -54. , -53.5])

In this example the operation is very fast, but with slower operations (e.g.
distance calculations) and large arrays, the time savings can be considerable
(see `lazyarray performance`_).
    
In summary, by using :class:`LazyArray`, we can pass parameters around in an
optimised way without having to worry about exactly what form the parameter
value takes, hence avoiding a lot of logic at multiple points in the code.

Reference
~~~~~~~~~

.. autoclass:: pyNN.parameters.LazyArray
   :members:
   :undoc-members:
   :inherited-members:
   :show-inheritance:

   .. automethod:: pyNN.parameters.LazyArray.__getitem__



The :class:`ParameterSpace` class
---------------------------------

:class:`ParameterSpace` is a dict-like class that contains :class:`LazyArray`
objects.

In addition to the usual :class:`dict` methods, it has several methods
that allow operations on all the lazy arrays within it at once. For example:

.. doctest::

    >>> from pyNN.parameters import ParameterSpace
    >>> ps = ParameterSpace({'a': [2, 3, 5, 8], 'b': 7, 'c': lambda i: 3*i+2}, shape=(4,))
    >>> ps['c']
    <larray: base_value=<function <lambda> at ...> shape=(4,) dtype=None, operations=[]> # doctest: +ELLIPSIS
    >>> ps.evaluate()
    >>> ps['c']
    array([ 2,  5,  8, 11])

the :meth:`evaluate()` method also accepts a mask, in order to evaluate only
part of the lazy arrays:

.. doctest::

    >>> ps = ParameterSpace({'a': [2, 3, 5, 8, 13], 'b': 7, 'c': lambda i: 3*i+2}, shape=(5,))
    >>> ps.evaluate(mask=[1, 3, 4])
    >>> ps.as_dict()
    {'a': array([ 3,  8, 13]), 'c': array([ 5, 11, 14]), 'b': array([7, 7, 7])}

An example with two-dimensional arrays:

.. doctest::

    >>> ps2d = ParameterSpace({'a': [[2, 3, 5, 8, 13], [21, 34, 55, 89, 144]],
    ...                        'b': 7,
    ...                        'c': lambda i, j: 3*i-2*j}, shape=(2, 5))
    >>> ps2d.evaluate(mask=(slice(None), [1, 3, 4]))
    >>> print(ps2d['a'])
    [[  3   8  13]
     [ 34  89 144]]
    >>> print(ps2d['c'])
    [[-2 -6 -8]
     [ 1 -3 -5]]

There are also several methods to allow iterating over the parameter space in
different ways. A :class:`ParameterSpace` can be viewed as both a :class:`dict`
contaning arrays and as an array of dicts. Iterating over a parameter space
gives the latter view:

.. doctest::

    >>> for D in ps:
    ...   print(D)
    ...
    {'a': 3, 'c': 5, 'b': 7}
    {'a': 8, 'c': 11, 'b': 7}
    {'a': 13, 'c': 14, 'b': 7}

unlike for a :class:`dict`, where iterating over it gives the keys.
:meth:`items()` works as for a normal :class:`dict`:

.. doctest::

    >>> for key, value in ps.items():
    ...   print(key, "=", value)
    a = [ 3  8 13]
    c = [ 5 11 14]
    b = [7 7 7]

Reference
~~~~~~~~~

.. autoclass:: ParameterSpace
   :members:
   :undoc-members:

   .. automethod:: __getitem__
   .. automethod:: __iter__


The :class:`Sequence` class
---------------------------

.. autoclass:: Sequence
   :members:
   :undoc-members:

   .. automethod:: __mul__
   .. automethod:: __div__


.. _lazyarray: https://lazyarray.readthedocs.org/
.. _`lazyarray performance`: https://lazyarray.readthedocs.org/en/latest/performance.html