Multicompartmental modelling

Warning

The extensions to the API for multicompartmental models are an experimental feature. We encourage users to try them out, but at this stage we do not recommend publishing work using these extensions without very thorough checking and validation of results.

Cell types

As noted in Building networks: neurons, the “standard library” approach described above needs to be modified for morphologically and biophysically-detailed neuron models, as the number of possible combinations of morphologies, ion-channel models and synapse models is infinite.

Rather than a library of standard neuron models, we have a library of standard components (ion-channel models, synapse models) from which multi-compartment models can be composed by writing a simple class:

class MyNeuronModel(MultiCompartmentNeuron):
    ion_channels = {
        'pas': sim.PassiveLeak,  # standard library of ion channel models
        'na': sim.NaChannel,
        'kdr': sim.KdrChannel
    }
    post_synaptic_entities = {
        'AMPA': sim.CondExpPostSynapticResponse,  # standard library of synapse models
        'GABA_A': sim.CondExpPostSynapticResponse
    }

Note

here we are using ion channel and synapse models from a standard library, but we also plan to support LEMS (NeuroML) and NineML model specifications for this.

Parameterization

When it come to instantiating such a cell type in order to create a population, three extensions are needed when compared to the point-neuron case:

Hierarchical parameter sets

Ion channel and synapse parameters should be in a separate dictionary nested inside the main parameter dictionary.

More complex parameter types

For the standard library of point neurons, most of the parameters are floats, with Sequence being the only special case. For morphologically-detailed neurons, one of the parameters is a representation of an entire morphology, since different neurons within a population can be expected to have different morphologies.

Spatial parameterization within neuron morphologies

Point neurons may have parameter values that depend on the position of the neuron in space. Neurons with axons and dendrites may have parameters whose value depends on location within a single neuron.

Handling of morphologies and spatial parameterization are described in the next section.

Morphologies

A number of existing Python tools for handling neuronal morphologies already exist (e.g. libNeuroML, Brian 2, MorphIO, btmorph) In the interest of integrating with other open-source simulation tools and standards wherever possible, rather than reinventing the wheel (see Multicompartmental modelling with PyNN: design goals), we would like to make use of these tools within PyNN, and give users a choice of tools where there is overlap.

Our proposed implementation of this is to define an interface through a parent Morphology, which can be sub-classed for the different Python representations of neuronal morphology, e.g. NeuroMLMorphology, BrianMorphology.

This standard interface for working with morphologies allows us to define tools for spatial parameterization, and, more generally, for specifying points and regions within the neurites, in a format-independent way.

The general principle is to present an interface to the user that treats the neuron as a continuous tree structure, and hides details of the spatial discretization, while mapping points and regions defined by the user to the particular section/segment used internally by the simulator.

Selecting points and regions

For simple morphologies, each section/segment/compartment of the neuron can be given an explicit label. Where we wish to distinguish a point within a section, we can add a fraction_along parameter. By default, a label is taken to refer to the centre of a section.

Thus, for example, if we specify a two-compartment neuron using NeuroML:

from neuroml import Segment, Point3DWithDiam as P
from pyNN.morphology import NeuroMLMorphology

soma = Segment(proximal=P(x=0, y=0, z=0, diameter=18.8),
               distal=P(x=18.8, y=0, z=0, diameter=18.8),
               name="soma")
dend = Segment(proximal=P(x=0, y=0, z=0, diameter=2),
               distal=P(x=-500, y=0, z=0, diameter=2),
               name="dendrite",
               parent=soma)

cell_type = cell_class(morphology=NeuroMLMorphology(segments=(soma, dend)),
                       ...other parameters...)
cells = sim.Population(n, cell_type)

Then we can, for example, specify where to inject a current using the segment labels:

step_current = sim.DCSource(amplitude=0.1, start=50.0, stop=150.0)
step_current.inject_into(cells[0:1], location="soma")
step_current.inject_into(cells[1:2], location="dendrite")

or, similarly, where to record the membrane voltage:

cells.record('v', locations=['soma', 'dendrite'])

For detailed morphologies it is less easy/useful to address/label specific sections, with the exception of the soma and perhaps the axon initial segment. We create such a morphology using, for example:

from pyNN.morphology import load_morphology

pyr_morph = load_morphology("oi15rpy4-1.CNG.swc")

class PyramidalCell(MultiCompartmentNeuron):
    ion_channels = {
        'pas': sim.PassiveLeak,
        'na': sim.NaChannel,
        'kdr': sim.KdrChannel
    }
    ...

pyramidal_cell_type = PyramidalCell(morphology=pyr_morph,
                                    ...other parameters...)
pyramidal_cells = sim.Population(n, pyramidal_cell_type)

We can select regions of the axonal and/or dendritic trees using a morphology filter. At present, the available filters are soma, apical_dendrites, basal_dendrites, dendrites, axon, and all.

Note

identifying apical and basal dendrites depends on the morphology description file providing these labels. This is the case for SWC files, and it is also supported by NeuroML.

Within these regions, we can specify parameters whose values depend on the position within the region, for example, distance along the dendrites from the soma:

kdr_conductance_density_apical = by_distance(apical_dendrites(), lambda d: 0.05 * d / 200.0)
kdr_conductance_density_basal = uniform(basal_dendrites(), lambda d: 0.07)

To specify specific points within a region (for injecting current, placing synapses or recording state variables) we specify a “location generator”:

locations = at_distances(apical_dendrites(), [50, 100, 150, 200, 250])  # distances in µm

Note that this may specify more than five points, due to branching (i.e. there will be a point on each branch at 100 µm from the soma); equally, some branches may be shorter than 250 µm and so no point will be generated for that branch.

In addition to at_distances, PyNN currently provides centre and random_placement; points can also be defined using labels provided in the morphology definition. Further location generator methods are planned.

Projections

In making synaptic connections onto neurons with detailed morphologies, we have to consider not only which post-synaptic neuron to connect to and which synapse type to use, but also in which location within the morphology the synapse should be.

There are two possible strategies when placing the post-synaptic component of synapses on neurons:

  1. place the post-synaptic mechanisms when creating the population (parameterized in the cell type), then when making connections select from this pre-existing list;

  2. place the post-synaptic mechanisms at the moment of creating connections.

Both strategies are in fairly widespread use, so it would be worthwhile to support both approaches in PyNN.

For Strategy 1, we specify the synaptic density when creating the cell type, as seen in the “pyramidal_cell” example above. It should also be possible to specify the placement of individual synapses in labelled sections, in the simple, few-compartment case (todo).

For Strategy 2, post-synaptic parameters are specified in the Connector class, see next section.

In making synaptic connections from neurons with detailed morphologies, we need to consider the location of the source of the action potential. Where the axon is represented explicitly, this location could be any point in the axon (terminal section or en-passant). Where the axon is not represented explicitly or is truncated, this location will be the soma, action initial segment, or terminal point of the truncation. In this case the synaptic delay should include a distance-dependent component, where the distance could be as-the-crow-flies, or the path length along the original axon (before truncation). In both cases, this is specified using the source argument of the Projection constructor.

Connectors

As noted above, for a given type of synapse, we have to consider both which post-synaptic neuron to connect to and in which location within the morphology the synapse should be.

In principle, these two choices could be independent, or the choice of location could depend on which post-synaptic neuron was chosen.

To support the second, more general case, we propose that the PyNN Connector classes should implement the algorithms for both choices in a single class. The first case, of independent choices, is likely to be fairly common however, so we also propose to extend the standard connectors from earlier versions of PyNN with a location_selector argument, so for example we might create a projection with:

sim.Projection(inputs, pyramidal_cells,
               connector=sim.AllToAllConnector(location_selector=sample(apical_dendrites())),
               synapse_type=sim.StaticSynapse(weight=0.5, delay=0.5),
               receptor_type="AMPA")

This will connect all inputs to all pyramidal cells, selecting one synapse location at random from the apical dendrites (this random location would be different for each neuron in the pyramidal cell population).

If Strategy 1 (as described above) is being used, the random sample is over those post-synaptic mechanisms already placed in the apical dendrites.

If Strategy 2 is being used, the random sample is over all possible locations in the apical dendrites, and the post-synaptic mechanism is inserted at the same time as the connection is created. In this scenario, it should be possible to set post-synaptic model and parameters at the same time, for example:

sim.Projection(inputs, pyramidal_cells,
           connector=sim.AllToAllConnector(location_selector=sample(apical_dendrites())),
           synapse_type=sim.StaticSynapse(weight=0.5, delay=0.5),
           receptor_type=sim.CondExpPostSynapticResponse(tau_syn=RandomDistribution('normal', (0.2, 0.01))))