API documentation: | spikes | analysis | benchmark | parameters | stgen | utilities | facets |

The parameters module

We consider it to be best practice to cleanly separate the parameters of a model from the model itself. At the least, parameters should be defined in a separate section at the start of a file. Ideally, they should be defined in a separate file entirely. This makes version control cleaner, as model vs parameter changes can be conceptually seperated, and makes it easier to track a simulation project, since the parameter sets can be stored in a database, displayed in a GUI, etc.

Parameters

At their simplest, individual parameters consist of a name and a value. The value is either a simple type such as a numerical value or a string, or an aggregate of such simple types, such as a set, list or array.

However, we may also wish to specify the physical dimensions of the parameter, i.e., its units, and the range of permissible values.

It is also often useful to specify an object that generates numerical values or strings, such as a random number generator, and treat that object as the parameter.

To support all these uses, we define the Parameter and ParameterRange classes, and various subclasses of the ParameterDist abstract class, such as GammaDist, NormalDist and UniformDist.

The Parameter class

Here are some examples of creating Parameter objects:

>>> i1 = Parameter(3)
>>> f1 = Parameter(6.2)
>>> f2 = Parameter(-65.3, "mV")
>>> s1 = Parameter("hello", name="message_to_the_world")

The parameter name, units, value and type can be accessed as attributes:

>>> i1.value
3
>>> f1.type
<type 'float'>
>>> f2.units
'mV'
>>> s1.name
'message_to_the_world'

Parameter objects are not hugely useful at the moment. The units are not used for checking dimensional consistency, for example, and Parameter objects are not drop-in replacements for numerical values - you must always use the value attribute to access the value, whereas it might be nice to define, for example, a class IntegerParameter which was a subclass of the built-in int type.

The ParameterRange class

When investigating the behaviour of a model or in doing sensitivity analysis, it is often useful to run a model several times using a different value for a certain parameter each time (also see the iter_range_keys() and similar methods of the ParameterSet class, below). The ParameterRange class supports this. Some usage examples:

>>> tau_m_range = ParameterRange([10.0, 15.0, 20.0], "ms", "tau_m")
>>> tau_m_range.name
'tau_m'
>>> tau_m_range.next()
10.0
>>> tau_m_range.next()
15.0
>>> [2*tau_m for tau_m in tau_m_range]
[20.0, 30.0, 40.0]

The ParameterDist classes

As with taking parameter values from a series or range, it is often useful to pick values from a particular random distribution. Three classes are available: UniformDist, GammaDist and NormalDist. Examples:

>>> ud = UniformDist(min=-1.0, max=1.0)
>>> gd = GammaDist(mean=0.5, std=1.0)
>>> nd = NormalDist(mean=-70, std=5.0)
>>> ud.next()
array([-0.56342352])
>>> gd.next(3)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "parameters.py", line 521, in next
    raise Exception('Error scipy was not found at import time.  GammaDist realization disabled.')
Exception: Error scipy was not found at import time.  GammaDist realization disabled.
>>> nd.next(2)
array([-76.18506715, -68.71229944])

[Note that very similar functionality is available with the RandomDistribution class in the pyNN.random module. We should look at the best ways to avoid duplication].

Parameter sets

A problem with parameter sets for large-scale, detailed models is that the list of parameters gets very long and unwieldy, and due to the typically hierarchical nature of such models, the individual parameter names can also get very long, e.g., v1_layer5_pyramidal_apical_dend_gbar_na.

A solution to this is to give the parameter set a hierarchical structure as well, which allows the top-level list of parameters to be very short (e.g. v1, retina and lgn for a visual system simulation) since the top-level parameters are themselves parameter sets.

The simplest way to implement this in Python is using nested dicts. One disadvantage of this is that accessing deeply-nested parameters can be very verbose, e.g. v1['layer5']['pyramidal']['apical_dend']['na']['gbar']. A second disadvantage is that it is tedious to flatten the hierarchy when this becomes necessary, e.g. for serialisation - writing to file, etc.

For these reasons we have created a ParameterSet class, which:

  1. allows a more convenient notation (e.g., v1.layer5.pyramidal.apical_dend.na.gbar, which requires only a single . for each level in the hierarchy rather than two "'"s, a "[" and a "]". This is not much shorter than v1_layer5_pyramidal_apical_dend_gbar_na - the difference is that v1.layer5.pyramidal is itself a ParameterSet object that can be passed as an argument to the pyramidal cell object, which doesn't care about v1.layer4.spinystellate, let alone retina.ganglioncell.magno.tau_m, while v1_layer5_pyramidal is just a NameError).
  2. provides convenient methods for iterating over the parameter set in various ways and reading from/writing to file.

The ParameterSet class

Creation

ParameterSet objects may be created from a dict:

>>> sim_params = ParameterSet({'dt': 0.11, 'tstop': 1000.0})

or loaded from a URL:

>>> exc_cell_params = ParameterSet("https://neuralensemble.org/svn/NeuroTools/trunk/doc/example.param")

They may be nested:

>>> inh_cell_params = ParameterSet({'tau_m': 15.0, 'cm': 0.5})
>>> network_params = ParameterSet({'excitatory_cells': exc_cell_params, 'inhibitory_cells': inh_cell_params})
>>> P = ParameterSet({'sim': sim_params, 'network': network_params}, label="my_params")

Note that although we show here only numerical parameter values, Parameter, ParameterRange and ParameterDist objects, as well as strings, may also be parameter values.

Viewing and saving

To see the entire parameter set at once, nicely formatted use the pretty() method:

>>> print P.pretty()
{
  "network": {
    "excitatory_cells": url("https://neuralensemble.org/svn/NeuroTools/trunk/doc/example.param")
    "inhibitory_cells": {
      "tau_m": 15.0,
      "cm": 0.75,
    },
  },
  "sim": {
    "tstop": 1000.0,
    "dt": 0.11,
  },
}

By default, if the ParameterSet contains other ParameterSets that were loaded from URLs, these will be represented with a url() function in the output, but there is also the option to expand all URLs and show the full contents:

>>> print P.pretty(expand_urls=True)
{
  "network": {
    "excitatory_cells": {
      "tau_refrac": 0.11,
      "tau_m": 10.0,
      "cm": 0.25,
      "synI": {
        "tau": 10.0,
        "E": -75.0,
      },
      "synE": {
        "tau": 1.5,
        "E": 0.0,
      },
      "v_thresh": -57.0,
      "v_reset": -70.0,
      "v_rest": -70.0,
    },
    "inhibitory_cells": {
      "tau_m": 15.0,
      "cm": 0.75,
    },
  },
  "sim": {
    "tstop": 1000.0,
    "dt": 0.11,
  },
}

If a ParameterSet was loaded from a URL, it may be modified then saved back to the same URL, provided the protocol supports writing:

>>> exc_cell_params.save()
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  File "parameters.py", line 266, in save
    raise Exception("Saving using the %s protocol is not implemented" % scheme)
Exception: Saving using the https protocol is not implemented

or saved to a different URL:

>>> exc_cell_params.save(url="file:///tmp/exc_params")

The file format is the same as that produced by the pretty() method.

Copying and converting

A ParameterSet can be used simply as a dictionary, but can also be converted explicitly to a dict if required:

>>> print sim_params.as_dict()
{'tstop': 1000.0, 'dt': 0.11}

[need to say something about tree_copy()]

Iteration

There are several different ways to iterate over all or part of the ParameterSet object. keys(), values() and items() work as for dict``s. For the sake of more readable code, ``names() is provided as an alias for keys() and parameters() as an alias for items():

>>> P.names()
['network', 'sim']
>>> exc_cell_params.parameters()
[('tau_refrac', 0.11), ('tau_m', 10.0), ('cm', 0.25),
 ('synI', {'tau': 10.0, 'E': -75.0}), ('synE', {'tau': 1.5, 'E': 0.0}),
 ('v_thresh', -57.0), ('v_reset', -70.0), ('v_rest', -70.0)]

To flatten nested parameter sets, i.e., the iterate recursively over all branches of the tree, the the flatten() method returns a dict with keys created by joining the names at each hierarchical level with a separator character ('.' by default):

>>> network_params.flatten()
{'excitatory_cells.synI.E': -75.0, 'excitatory_cells.v_rest': -70.0,
 'excitatory_cells.tau_refrac': 0.11, 'excitatory_cells.v_reset': -70.0,
 'excitatory_cells.v_thresh': -57.0, 'excitatory_cells.tau_m': 10.0,
 'excitatory_cells.synI.tau': 10.0, 'excitatory_cells.cm': 0.25,
 'inhibitory_cells.cm': 0.75, 'excitatory_cells.synE.tau': 1.5,
 'excitatory_cells.synE.E': 0.0, 'inhibitory_cells.tau_m': 15.0}

while the flat() method returns a generator which yields (name, value) tuples.:

>>> for x in network_params.flat():
...   print x
...

Several further methods are applicable if the ParameterSet contains ParameterRange or ParameterDist objects [to be completed].

This document was last updated for r126.

API

Data

__name__ = NeuroTools.parameters

have_scipy = False

Functions

isiterable(x)

nesteddictflatten(d, separator='.')

Return a flattened version of a nested dict structure.

Composite keys are created by joining each key to the key of the parent dict using separator.

nesteddictwalk(d, separator='.')

Walk a nested dict structure, using a generator.

Composite keys are created by joining each key to the key of the parent dict using separator.

read_parameters(filename)

Read parameters from a text file.

test()

urlparse(url, scheme=, allow_fragments=1)

Parse a URL into 6 components: <scheme>://<netloc>/<path>;<params>?<query>#<fragment> Return a 6-tuple: (scheme, netloc, path, params, query, fragment). Note that we don't break the components up in smaller bits (e.g. netloc is a single string) and we don't expand % escapes.

Classes

GammaDist

gamma.pdf(x,a,b) = x**(a-1)*exp(-x/b)/gamma(a)/b**a

Yields strictly positive numbers!

Generally the distribution is implemented by scipy.stats.gamma.pdf(x/b,a)/b

For more info, in ipython type:

? scipy.stats.gamma

__init__(self, mean=None, std=None, repr_mode='ms', **params)

repr_mode specifies how the dist is displayed, either mean,var ('ms', the default) or a,b ('ab')

from_stats(self, vals, bias=0.0, expand=1.0)

mean(self)

next(self, n=1)

std(self)


NormalDist

normal distribution with parameters mean + std

Generally the distribution is implemented by scipy.stats.gamma.pdf(x/b,a)/b

For more info, in ipython type:

? scipy.stats.gamma

__init__(self, mean=0.0, std=1.0)

from_stats(self, vals, bias=0.0, expand=1.0)

next(self, n=1)


Parameter

__init__(self, value, units=None, name=)


ParameterDist

__init__(self, **params)

from_stats(self, vals, bias=0.0, expand=1.0)

next(self, n=1)


ParameterRange

A class for specifying a list of possible parameters for a given parameter

The value must be an iterable

It acts like a Parameter, but .next() can be called to iterate through the values

__init__(self, value, units=None, name=)

__iter__(self)

next(self)


ParameterSet

A class to manage hierarchical parameter sets.

Usage example:

sim_params = ParameterSet({'dt': 0.1, 'tstop': 1000.0}) exc_cell_params = ParameterSet("http://neuralensemble.org/svn/!NeuroTools/example.params") inh_cell_params = ParameterSet({'tau_m': 15.0, 'cm': 0.5}) network_params = ParameterSet({'excitatory_cells': exc_cell_params, 'inhibitory_cells': inh_cell_params}) P = ParameterSet({'sim': sim_params, 'network': network_params}) P.sim.dt

0.1

P.network.inhibitory_cells.tau_m

15.0

print P.pretty()

__getattr__(self, name)

Allow accessing parameters using dot notation.

__getitem__(self, name)

Modified get that detects dots '.' in the names and goes down the nested tree to find it

__init__(self, initialiser, label=None)

__setitem__(self, name, value)

Modified set that detects dots '.' in the names and goes down the nested tree to set it

as_dict(self)

returns a copy of the ParameterSet tree structure as a nested dictionary

dist_keys(self)

returns the list of keys for those elements which are ParameterDists

iter_inner(self, copy=False)

An iterator of the ParameterSet which yields the ParameterSet with all combinations of ParameterRange elements

iter_inner_range_keys(self, keys, copy=False)

An iterator of the ParameterSet which yields the ParameterSet with all combinations of ParameterRange elements which are given by the keys list

Note: each newly yielded value is one and the same object so storing the returned values results in a collection of many of the lastly yielded object.

copy=True causes each yielded object to be a newly created object, but be careful because this is spawning many dictionaries!

iter_range_key(self, range_key)

An iterator of the ParameterSet which yields the ParameterSet with the ParameterRange given by key replaced with each of its values

pretty(self, indent=' ', expand_urls=False)

Return a unicode string representing the structure of the !ParameterSet. evaluating the string should recreate the object.

range_keys(self)

returns the list of keys for those elements which are ParameterRanges

realize_dists(self, n=1, copy=False)

For each ParameterDist, realize the distribution and yield the result

If copy==True, causes each yielded object to be a newly created object, but be careful because this is spawning many dictionaries!

save(self, url=None, expand_urls=False)

Write the parameter set to a text file.

The text file syntax is open to discussion. My idea is that it should be valid Python code, preferably importable as a module.

If url is None, try to save to self.url (if it is not None), otherwise save to url.

tree_copy(self)

returns a copy of the ParameterSet tree structure. Nodes are not copied, but re-referenced. __cmp__ = <slot wrapper '__cmp__' of 'dict' objects>

__contains__ = <method '__contains__' of 'dict' objects>

__delitem__ = <slot wrapper '__delitem__' of 'dict' objects>

__eq__ = <slot wrapper '__eq__' of 'dict' objects>

__ge__ = <slot wrapper '__ge__' of 'dict' objects>

__gt__ = <slot wrapper '__gt__' of 'dict' objects>

__iter__ = <slot wrapper '__iter__' of 'dict' objects>

__le__ = <slot wrapper '__le__' of 'dict' objects>

__len__ = <slot wrapper '__len__' of 'dict' objects>

__lt__ = <slot wrapper '__lt__' of 'dict' objects>

__ne__ = <slot wrapper '__ne__' of 'dict' objects>

clear = <method 'clear' of 'dict' objects>

copy = <method 'copy' of 'dict' objects>

fromkeys = <built-in method fromkeys of type object at 0x33fc80>

get = <method 'get' of 'dict' objects>

has_key = <method 'has_key' of 'dict' objects>

items = <method 'items' of 'dict' objects>

iteritems = <method 'iteritems' of 'dict' objects>

iterkeys = <method 'iterkeys' of 'dict' objects>

itervalues = <method 'itervalues' of 'dict' objects>

keys = <method 'keys' of 'dict' objects>

non_parameter_attributes = ['url', 'label', 'names', 'parameters', 'flat', 'flatten', 'non_parameter_attributes']

pop = <method 'pop' of 'dict' objects>

popitem = <method 'popitem' of 'dict' objects>

setdefault = <method 'setdefault' of 'dict' objects>

update = <method 'update' of 'dict' objects>

values = <method 'values' of 'dict' objects>


UniformDist

uniform distribution with min,max

__init__(self, min=0.0, max=1.0)

from_stats(self, vals, bias=0.0, expand=1.0)

next(self, n=1)