Basic Structure of NineML Abstraction Layer¶
In this tutorial, we build the Izhikevich model neuron. It is defined by the following dynamics:
\frac{dV}{dt} = 0.04V^2 + 5V + 140 -u + I \frac{du}{dt} = a(bV -u)
where if v> 30mV then we have a spike
v \leftarrow c u \leftarrow u + d
where a,b,c,d are parameters of our neuron and I is the injected current. V and U are state variables, which need to be solved over time.
Interfaces: Parameters and Ports¶
We begin by defining the interface to our neuron. The interface is composed of
- Parameters: values used to instantiate a component of a particular type. In this case, these would be
a, b, c and d. Parameters are set once at the beginning of a simulation.
- Ports: which allow the component to communicate with other components
during the simulation. Ports are divided into two categories:
- Event ports, which transmit or receive single, discrete events at
points in time. For example, an event could represent a neuron spiking.
- Analog ports, which transmit or receive continuous signals, for
example the membrane voltage of the neuron.
Furthermore, ports have a
direction, specifying whether they represent information coming from the componentsend, or information flowing into the component,recv(Andreduce, which will be discussed later.)
In this case, the neuron receives an injected current I, which will be a
recv Analog-port. Other components (such as synapses) may be interested in
the neuron’s voltage, V, so we should transmit this as a send Analog-port.
When the neuron reaches the condition for firing (v> 30mV), we may also
want to notify other components about this event, so we also have a send
Event-port.
We can build a ComponentClass with this interface with the following code:
If you try running this code, you will receive the following error:
nineml.exceptions.exceptions.NineMLRuntimeError: Unable to find an Alias or State variable for analog-port: V
This is because we have defined a component and promised that we will transmit a value over the port V, but we have not defined V anywhere. We will fix this next.
Dynamics: Regimes & StateVariables¶
Now that we have defined the interface of the ComponentClass, we now need
to define the internal dynamics of the system, to give it some behaviour. A
ComponentClass can contain StateVariables, which are variables that
describe the internal state of the neuron. Typically, these are specified by
first-order-differential equations with-respect-to time. In our example, the
Izhikevich model has 2 state-variables, U and V.
The state-variables can have different behaviours when operating in different Regimes. A regime can be considered the ‘mode’ of the component; at any time, the component will be in a single ‘regime’, and it is possible to move between regimes. for example, an integrate-and-fire neuron with an explicit refractory period could be modelled as a component with two regimes, a default regime, where injected current affects membrane voltage, where and a second refractory regime where the voltage is fixed to a certain value.
For this model, the differential-equations governing the state variables never change, so we only need a single regime.
In this case, we have specified the state-variables for this component by
explicitly providing a list of the state-variables to the Dynamics blocks.
This is not essential, if it is not provided, it will automatically be inferred
from the state-variable definitions in the Regimes, but if it is given, it must
match exactly.
This code should now run; but we are missing the condition, v> 30mV.
Transitions: Events, Conditions & Assignments¶
We have discussed that component can contain multiple Regimes. In order to
move between regimes; we introduce the idea of Transition s. A transition
can be:
- OnEvent - A transition triggered by an event arriving on a
recvEventPort.- OnCondition - A transition triggered by a condition.
When a transition occurs, three things can optionally occur:
- An event can be emitted on a
sendEventPort, for example, when a membrane voltage reaches a threshold values, we may want to send an event to signal a spike occurring.- StateVariables can be changes through
StateAssignment. For example, a transition in a synapse component may cause the post-synaptic conductance to increase by a fixed amount.- The component can switch to another regime; i.e. respond to another set of differential equations.
For the Izhikevich model, we will use an ‘OnCondition’ transition, which should update the state-variables, U and V according to the equations:
v \leftarrow c u \leftarrow u + d
We will also emit a spike on the EventPort spikeoutput, as this might be
useful if we want to use this component as part of a larger system. Since we
only have a single regime, we will not change regime.
Multiple Regimes & Transitions¶
We have only discussed the case of a single regime. A leaky integrate-and-fire model with refractory period has two dynamical regimes - the sub-threshold regime and the refractory regime. Just for fun, we’ll define the component in a single step:
Note that here we used the name of the regime in the to argument to the
On transition constructor, rather than a Regime object. These references
are resolved automatically when the component is built.
If the differential equations for a StateVariable are not defined within a
regime, then it is assumed that the state-variable does not change in that
Regime, i.e. d/dt = 0.
Further Classes¶
Aliases¶
Aliases are motivated by 2 cases; firstly that we would like to be able to
send something other than pure StateVariables, and that often we end
up re-using calculations. For example, if we want to define a conductance-based
synapse in NineML, then we would like to specify the current in the
post-synaptic neuron.
Note
When specifying Aliases, we use the syntax := instead of =
In this case, we define an Alias, I, which can used in a send port.
Aliases can also be used on the right-hand-side of other aliases, Condition
s, StateAssignment s and TimeDerivative s.
Reduce Ports¶
We have discussed send and recv ports, but there is another
port-mode, which is reduce. A reduce port is also a port that takes
in data; but it can take information from multiple send ports. A typical
example might be the injected current into a neuron. Current can come into a
neuron from current injection, synapses or membrane channels. A recv port
is not sufficient in this case, because a recv port can only take
information from one other send port. Instead, we use a reduce port,
which takes an additional parameter reduce_op. This specifies how the
incoming data should be defined. For example, to calculate the total current
flowing into a cell, we would add all the current sources together, so we
would create the port as:
p = AnalogPort(name="I", mode='reduce', reduce_op='+')
See the docs for AnalogPort for more information.
Specifying Mathematical Strings¶
When specifying mathematics, we use a notation similar to C/C++. That is:
(3B + 1)V^2
is not valid, it should be written as:
V * V * (3*B + 1)
Depending on what is being specified, we specify the mathematics slightly differently:
Aliases should be of the form:
alias := some * equationTimeDerivatives for a state-variable, S, should be of the form:
dS/dt = some * equationStateAssignments must be written out in full; there is no in-place operators:
g += q # Invalid g = g + q # Valid