| 1 |
========================= |
|---|
| 2 |
The ``parameters`` module |
|---|
| 3 |
========================= |
|---|
| 4 |
|
|---|
| 5 |
We consider it to be best practice to cleanly separate the parameters of a model |
|---|
| 6 |
from the model itself. At the least, parameters should be defined in a separate |
|---|
| 7 |
section at the start of a file. Ideally, they should be defined in a separate |
|---|
| 8 |
file entirely. This makes version control easier, since the model code typically |
|---|
| 9 |
changes less often than the parameters, and makes it easier to track a |
|---|
| 10 |
simulation project, since the parameter sets can be stored in a database, |
|---|
| 11 |
displayed in a GUI, etc. |
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
---------- |
|---|
| 15 |
Parameters |
|---|
| 16 |
---------- |
|---|
| 17 |
|
|---|
| 18 |
At their simplest, individual parameters consist of a name and a value. The |
|---|
| 19 |
value is either a simple type such as a numerical value or a string, or an |
|---|
| 20 |
aggregate of such simple types, such as a set, list or array. |
|---|
| 21 |
|
|---|
| 22 |
However, we may also wish to specify the physical dimensions of the parameter, |
|---|
| 23 |
i.e., its units, and the range of permissible values. |
|---|
| 24 |
|
|---|
| 25 |
It is also often useful to specify an object that generates numerical values or |
|---|
| 26 |
strings, such as a random number generator, and treat that object as the |
|---|
| 27 |
parameter. |
|---|
| 28 |
|
|---|
| 29 |
To support all these uses, we define the ``Parameter`` and ``ParameterRange`` |
|---|
| 30 |
classes, and various subclasses of the ``ParameterDist`` abstract class, such as |
|---|
| 31 |
``GammaDist``, ``NormalDist`` and ``UniformDist``. |
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
The ``Parameter`` class |
|---|
| 35 |
----------------------- |
|---|
| 36 |
|
|---|
| 37 |
Here are some examples of creating ``Parameter`` objects:: |
|---|
| 38 |
|
|---|
| 39 |
>>> i1 = Parameter(3) |
|---|
| 40 |
>>> f1 = Parameter(6.2) |
|---|
| 41 |
>>> f2 = Parameter(-65.3, "mV") |
|---|
| 42 |
>>> s1 = Parameter("hello", name="message_to_the_world") |
|---|
| 43 |
|
|---|
| 44 |
The parameter name, units, value and type can be accessed as attributes:: |
|---|
| 45 |
|
|---|
| 46 |
>>> i1.value |
|---|
| 47 |
3 |
|---|
| 48 |
>>> f1.type |
|---|
| 49 |
<type 'float'> |
|---|
| 50 |
>>> f2.units |
|---|
| 51 |
'mV' |
|---|
| 52 |
>>> s1.name |
|---|
| 53 |
'message_to_the_world' |
|---|
| 54 |
|
|---|
| 55 |
``Parameter`` objects are not hugely useful at the moment. The units are not |
|---|
| 56 |
used for checking dimensional consistency, for example, and ``Parameter`` |
|---|
| 57 |
objects are not drop-in replacements for numerical values - you must always use |
|---|
| 58 |
the ``value`` attribute to access the value, whereas it might be nice to define, |
|---|
| 59 |
for example, a class ``IntegerParameter`` which was a subclass of the built-in |
|---|
| 60 |
``int`` type. |
|---|
| 61 |
|
|---|
| 62 |
|
|---|
| 63 |
The ``ParameterRange`` class |
|---|
| 64 |
---------------------------- |
|---|
| 65 |
|
|---|
| 66 |
When investigating the behaviour of a model or in doing sensitivity analysis, it |
|---|
| 67 |
is often useful to run a model several times using a different value for a |
|---|
| 68 |
certain parameter each time (also see the ``iter_range_keys()`` and similar |
|---|
| 69 |
methods of the ``ParameterSet`` class, below). The ``ParameterRange`` class |
|---|
| 70 |
supports this. Some usage examples:: |
|---|
| 71 |
|
|---|
| 72 |
>>> tau_m_range = ParameterRange([10.0, 15.0, 20.0], "ms", "tau_m") |
|---|
| 73 |
>>> tau_m_range.name |
|---|
| 74 |
'tau_m' |
|---|
| 75 |
>>> tau_m_range.next() |
|---|
| 76 |
10.0 |
|---|
| 77 |
>>> tau_m_range.next() |
|---|
| 78 |
15.0 |
|---|
| 79 |
>>> [2*tau_m for tau_m in tau_m_range] |
|---|
| 80 |
[20.0, 30.0, 40.0] |
|---|
| 81 |
|
|---|
| 82 |
|
|---|
| 83 |
The ``ParameterDist`` classes |
|---|
| 84 |
----------------------------- |
|---|
| 85 |
|
|---|
| 86 |
As with taking parameter values from a series or range, it is often useful to |
|---|
| 87 |
pick values from a particular random distribution. Three classes are available: |
|---|
| 88 |
``UniformDist``, ``GammaDist`` and ``NormalDist``. Examples:: |
|---|
| 89 |
|
|---|
| 90 |
>>> ud = UniformDist(min=-1.0, max=1.0) |
|---|
| 91 |
>>> gd = GammaDist(mean=0.5, std=1.0) |
|---|
| 92 |
>>> nd = NormalDist(mean=-70, std=5.0) |
|---|
| 93 |
>>> ud.next() |
|---|
| 94 |
array([-0.56342352]) |
|---|
| 95 |
>>> gd.next(3) |
|---|
| 96 |
array([ 0.04061142, 0.05550265, 0.23469344]) |
|---|
| 97 |
>>> nd.next(2) |
|---|
| 98 |
array([-76.18506715, -68.71229944]) |
|---|
| 99 |
|
|---|
| 100 |
[Note that very similar functionality is available with the ``RandomDistribution`` |
|---|
| 101 |
class in the `pyNN.random` module. We should look at the best ways to avoid |
|---|
| 102 |
duplication]. |
|---|
| 103 |
|
|---|
| 104 |
|
|---|
| 105 |
-------------- |
|---|
| 106 |
Parameter sets |
|---|
| 107 |
-------------- |
|---|
| 108 |
|
|---|
| 109 |
A problem with parameter sets for large-scale, detailed models is that the list |
|---|
| 110 |
of parameters gets very long and unwieldy, and due to the typically hierarchical |
|---|
| 111 |
nature of such models, the individual parameter names can also get very long, |
|---|
| 112 |
e.g., ``v1_layer5_pyramidal_apical_dend_gbar_na``. |
|---|
| 113 |
|
|---|
| 114 |
A solution to this is to give the parameter set a hierarchical structure as well, |
|---|
| 115 |
which allows the top-level list of parameters to be very short (e.g. ``v1``, |
|---|
| 116 |
``retina`` and ``lgn`` for a visual system simulation) since the top-level |
|---|
| 117 |
parameters are themselves parameter sets. |
|---|
| 118 |
|
|---|
| 119 |
The simplest way to implement this in Python is using nested dicts. One |
|---|
| 120 |
disadvantage of this is that accessing deeply-nested parameters can be very |
|---|
| 121 |
verbose, e.g. ``v1['layer5']['pyramidal']['apical_dend']['na']['gbar']``. A |
|---|
| 122 |
second disadvantage is that it is tedious to flatten the hierarchy when |
|---|
| 123 |
this becomes necessary, e.g. for serialisation - writing to file, etc. |
|---|
| 124 |
|
|---|
| 125 |
For these reasons we have created a ``ParameterSet`` class, which: |
|---|
| 126 |
|
|---|
| 127 |
1. allows a more convenient notation; |
|---|
| 128 |
|
|---|
| 129 |
2. enables subsets of the parameters, lower in the hierarchy, to be passed |
|---|
| 130 |
around by themselves; |
|---|
| 131 |
|
|---|
| 132 |
3. provides convenient methods for reading from/writing to file and for |
|---|
| 133 |
determining the differences between two different parameter sets. |
|---|
| 134 |
|
|---|
| 135 |
An example of the notation is ``v1.layer5.pyramidal.apical_dend.na.gbar``, which |
|---|
| 136 |
requires only a single `.` for each level in the hierarchy rather than two |
|---|
| 137 |
"``'``"s, a "``[``" and a "``]``". This is not much shorter than |
|---|
| 138 |
``v1_layer5_pyramidal_apical_dend_gbar_na`` - the difference is that |
|---|
| 139 |
``v1.layer5.pyramidal`` is itself a ``ParameterSet`` object that can be passed |
|---|
| 140 |
as an argument to the pyramidal cell object, which doesn't care about |
|---|
| 141 |
``v1.layer4.spinystellate``, let alone ``retina.ganglioncell.magno.tau_m`` |
|---|
| 142 |
(while ``v1_layer5_pyramidal`` is just a ``NameError``). |
|---|
| 143 |
|
|---|
| 144 |
|
|---|
| 145 |
The ``ParameterSet`` class |
|---|
| 146 |
-------------------------- |
|---|
| 147 |
|
|---|
| 148 |
Creation |
|---|
| 149 |
~~~~~~~~ |
|---|
| 150 |
|
|---|
| 151 |
``ParameterSet`` objects may be created from a dict:: |
|---|
| 152 |
|
|---|
| 153 |
>>> sim_params = ParameterSet({'dt': 0.11, 'tstop': 1000.0}) |
|---|
| 154 |
|
|---|
| 155 |
or loaded from a URL:: |
|---|
| 156 |
|
|---|
| 157 |
>>> exc_cell_params = ParameterSet("https://neuralensemble.org/svn/NeuroTools/trunk/doc/example.param") |
|---|
| 158 |
|
|---|
| 159 |
They may be nested:: |
|---|
| 160 |
|
|---|
| 161 |
>>> inh_cell_params = ParameterSet({'tau_m': 15.0, 'cm': 0.5}) |
|---|
| 162 |
>>> network_params = ParameterSet({'excitatory_cells': exc_cell_params, 'inhibitory_cells': inh_cell_params}) |
|---|
| 163 |
>>> P = ParameterSet({'sim': sim_params, 'network': network_params}, label="my_params") |
|---|
| 164 |
|
|---|
| 165 |
Note that although we show here only numerical parameter values, |
|---|
| 166 |
``Parameter``, ``ParameterRange`` and ``ParameterDist`` objects, as well as |
|---|
| 167 |
strings, may also be parameter values. |
|---|
| 168 |
|
|---|
| 169 |
Navigation |
|---|
| 170 |
~~~~~~~~~~ |
|---|
| 171 |
|
|---|
| 172 |
Individual parameters may be accessed/set using dot notation:: |
|---|
| 173 |
|
|---|
| 174 |
>>> P.sim.dt |
|---|
| 175 |
0.11 |
|---|
| 176 |
>>> P.network.inhibitory_cells.tau_m |
|---|
| 177 |
15.0 |
|---|
| 178 |
>>> P.network.inhibitory_cells.cm = 0.75 |
|---|
| 179 |
|
|---|
| 180 |
or the usual dictionary access notation:: |
|---|
| 181 |
|
|---|
| 182 |
>>> P['network']['inhibitory_cells']['cm'] |
|---|
| 183 |
0.75 |
|---|
| 184 |
|
|---|
| 185 |
or mixing the two (which may be required if some of the parameter names contain |
|---|
| 186 |
spaces):: |
|---|
| 187 |
|
|---|
| 188 |
>>> P['network'].excitatory_cells['tau_m'] |
|---|
| 189 |
10.0 |
|---|
| 190 |
|
|---|
| 191 |
Viewing and saving |
|---|
| 192 |
~~~~~~~~~~~~~~~~~~ |
|---|
| 193 |
|
|---|
| 194 |
To see the entire parameter set at once, nicely formatted use the ``pretty()`` |
|---|
| 195 |
method:: |
|---|
| 196 |
|
|---|
| 197 |
>>> print P.pretty() |
|---|
| 198 |
{ |
|---|
| 199 |
"network": { |
|---|
| 200 |
"excitatory_cells": url("https://neuralensemble.org/svn/NeuroTools/trunk/doc/example.param") |
|---|
| 201 |
"inhibitory_cells": { |
|---|
| 202 |
"tau_m": 15.0, |
|---|
| 203 |
"cm": 0.75, |
|---|
| 204 |
}, |
|---|
| 205 |
}, |
|---|
| 206 |
"sim": { |
|---|
| 207 |
"tstop": 1000.0, |
|---|
| 208 |
"dt": 0.11, |
|---|
| 209 |
}, |
|---|
| 210 |
} |
|---|
| 211 |
|
|---|
| 212 |
By default, if the ``ParameterSet`` contains other ``ParameterSet``\s that were |
|---|
| 213 |
loaded from URLs, these will be represented with a ``url()`` function in the |
|---|
| 214 |
output, but there is also the option to expand all URLs and show the full |
|---|
| 215 |
contents:: |
|---|
| 216 |
|
|---|
| 217 |
>>> print P.pretty(expand_urls=True) |
|---|
| 218 |
{ |
|---|
| 219 |
"network": { |
|---|
| 220 |
"excitatory_cells": { |
|---|
| 221 |
"tau_refrac": 0.11, |
|---|
| 222 |
"tau_m": 10.0, |
|---|
| 223 |
"cm": 0.25, |
|---|
| 224 |
"synI": { |
|---|
| 225 |
"tau": 10.0, |
|---|
| 226 |
"E": -75.0, |
|---|
| 227 |
}, |
|---|
| 228 |
"synE": { |
|---|
| 229 |
"tau": 1.5, |
|---|
| 230 |
"E": 0.0, |
|---|
| 231 |
}, |
|---|
| 232 |
"v_thresh": -57.0, |
|---|
| 233 |
"v_reset": -70.0, |
|---|
| 234 |
"v_rest": -70.0, |
|---|
| 235 |
}, |
|---|
| 236 |
"inhibitory_cells": { |
|---|
| 237 |
"tau_m": 15.0, |
|---|
| 238 |
"cm": 0.75, |
|---|
| 239 |
}, |
|---|
| 240 |
}, |
|---|
| 241 |
"sim": { |
|---|
| 242 |
"tstop": 1000.0, |
|---|
| 243 |
"dt": 0.11, |
|---|
| 244 |
}, |
|---|
| 245 |
} |
|---|
| 246 |
|
|---|
| 247 |
If a ``ParameterSet`` was loaded from a URL, it may be modified then saved back |
|---|
| 248 |
to the same URL, provided the protocol supports writing:: |
|---|
| 249 |
|
|---|
| 250 |
>>> exc_cell_params.save() |
|---|
| 251 |
Traceback (most recent call last): |
|---|
| 252 |
File "<stdin>", line 1, in ? |
|---|
| 253 |
File "parameters.py", line 266, in save |
|---|
| 254 |
raise Exception("Saving using the %s protocol is not implemented" % scheme) |
|---|
| 255 |
Exception: Saving using the https protocol is not implemented |
|---|
| 256 |
|
|---|
| 257 |
or saved to a different URL:: |
|---|
| 258 |
|
|---|
| 259 |
>>> exc_cell_params.save(url="file:///tmp/exc_params") |
|---|
| 260 |
|
|---|
| 261 |
The file format is the same as that produced by the ``pretty()`` method. |
|---|
| 262 |
|
|---|
| 263 |
Copying and converting |
|---|
| 264 |
~~~~~~~~~~~~~~~~~~~~~~ |
|---|
| 265 |
|
|---|
| 266 |
A ``ParameterSet`` can be used simply as a dictionary, but can also be |
|---|
| 267 |
converted explicitly to a ``dict`` if required:: |
|---|
| 268 |
|
|---|
| 269 |
>>> print sim_params.as_dict() |
|---|
| 270 |
{'tstop': 1000.0, 'dt': 0.11} |
|---|
| 271 |
|
|---|
| 272 |
[need to say something about ``tree_copy()``] |
|---|
| 273 |
|
|---|
| 274 |
Iteration |
|---|
| 275 |
~~~~~~~~~ |
|---|
| 276 |
|
|---|
| 277 |
There are several different ways to iterate over all or part of the |
|---|
| 278 |
``ParameterSet`` object. ``keys()``, ``values()`` and ``items()`` work as for |
|---|
| 279 |
``dict``s. For the sake of more readable code, ``names()`` is provided as an |
|---|
| 280 |
alias for ``keys()`` and ``parameters()`` as an alias for ``items()``:: |
|---|
| 281 |
|
|---|
| 282 |
>>> P.names() |
|---|
| 283 |
['network', 'sim'] |
|---|
| 284 |
>>> exc_cell_params.parameters() |
|---|
| 285 |
[('tau_refrac', 0.11), ('tau_m', 10.0), ('cm', 0.25), |
|---|
| 286 |
('synI', {'tau': 10.0, 'E': -75.0}), ('synE', {'tau': 1.5, 'E': 0.0}), |
|---|
| 287 |
('v_thresh', -57.0), ('v_reset', -70.0), ('v_rest', -70.0)] |
|---|
| 288 |
|
|---|
| 289 |
To flatten nested parameter sets, i.e., the iterate recursively over all |
|---|
| 290 |
branches of the tree, the the ``flatten()`` method returns a ``dict`` with keys |
|---|
| 291 |
created by joining the names at each hierarchical level with a separator |
|---|
| 292 |
character ('.' by default):: |
|---|
| 293 |
|
|---|
| 294 |
>>> network_params.flatten() |
|---|
| 295 |
{'excitatory_cells.synI.E': -75.0, 'excitatory_cells.v_rest': -70.0, |
|---|
| 296 |
'excitatory_cells.tau_refrac': 0.11, 'excitatory_cells.v_reset': -70.0, |
|---|
| 297 |
'excitatory_cells.v_thresh': -57.0, 'excitatory_cells.tau_m': 10.0, |
|---|
| 298 |
'excitatory_cells.synI.tau': 10.0, 'excitatory_cells.cm': 0.25, |
|---|
| 299 |
'inhibitory_cells.cm': 0.75, 'excitatory_cells.synE.tau': 1.5, |
|---|
| 300 |
'excitatory_cells.synE.E': 0.0, 'inhibitory_cells.tau_m': 15.0} |
|---|
| 301 |
|
|---|
| 302 |
while the ``flat()`` method returns a generator which yields |
|---|
| 303 |
``(name, value)`` tuples.:: |
|---|
| 304 |
|
|---|
| 305 |
>>> for x in network_params.flat(): |
|---|
| 306 |
... print x |
|---|
| 307 |
... |
|---|
| 308 |
|
|---|
| 309 |
|
|---|
| 310 |
The ``ParameterSpace`` class |
|---|
| 311 |
---------------------------- |
|---|
| 312 |
|
|---|
| 313 |
The ``ParameterSpace`` class is a subclass of ``ParameterSet`` that is |
|---|
| 314 |
allowed to contain ``ParameterRange`` and ``ParameterDist`` objects as |
|---|
| 315 |
parameters. This turns the single point in parameter space represented by a |
|---|
| 316 |
``ParameterSet`` into a set of points. For example, the following definition |
|---|
| 317 |
creates a set of six points in parameter space, which can be obtained in turn |
|---|
| 318 |
using the ``iter_inner()`` method:: |
|---|
| 319 |
|
|---|
| 320 |
>>> PS = ParameterSpace({ |
|---|
| 321 |
... 'x': 999, |
|---|
| 322 |
... 'y': ParameterRange([10, 20]), |
|---|
| 323 |
... 'z': ParameterRange([-1, 0, 1]) |
|---|
| 324 |
... }) |
|---|
| 325 |
>>> for P in PS.iter_inner(): |
|---|
| 326 |
... print P |
|---|
| 327 |
{'y': 10, 'x': 999, 'z': -1} |
|---|
| 328 |
{'y': 20, 'x': 999, 'z': -1} |
|---|
| 329 |
{'y': 10, 'x': 999, 'z': 0} |
|---|
| 330 |
{'y': 20, 'x': 999, 'z': 0} |
|---|
| 331 |
{'y': 10, 'x': 999, 'z': 1} |
|---|
| 332 |
{'y': 20, 'x': 999, 'z': 1} |
|---|
| 333 |
|
|---|
| 334 |
Putting parameter distribution objects inside a ``ParameterSpace`` allows an |
|---|
| 335 |
essentially infinite number of points to be generated:: |
|---|
| 336 |
|
|---|
| 337 |
>>> PS2 = ParameterSpace({ |
|---|
| 338 |
... 'x': UniformDist(min=-1.0, max=1.0), |
|---|
| 339 |
... 'y': GammaDist(mean=0.5, std=1.0), |
|---|
| 340 |
... 'z': NormalDist(mean=-70, std=5.0) |
|---|
| 341 |
... }) |
|---|
| 342 |
>>> for P in PS2.realize_dists(n=3): |
|---|
| 343 |
... print P |
|---|
| 344 |
{'y': 1.81311773668, 'x': 0.883293989399, 'z': -73.5871002759} |
|---|
| 345 |
{'y': 0.299391158731, 'x': 0.371474054049, 'z': -68.6936045978} |
|---|
| 346 |
{'y': 2.90108202422, 'x': -0.388218831787, 'z': -68.6681724449} |
|---|
| 347 |
|
|---|
| 348 |
|
|---|
| 349 |
*This document was last updated for r274.* |
|---|