SpineML The Spiking Neural Mark-up Language

Component tutorial

This article is one of a series of tutorials designed to provide the new user with insight into how to create models using the GUI. Advanced users may find the Reference section more helpful. Note that the screenshots are from the Mac build of the GUI, and will have a different visual appearance on other platforms.

Introduction to the Component Editor

component_tab Fig1: Tab selector

The component editor tab is selected using the tab bar on the left side of the GUI window. The Fig 1 shows the tab bar with the Component editor tab selected. You should now select this tab.

Once the tab is selected, the component editor is laid out as in Fig

  1. The colours highlight the following sections:

  2. Tab selector: This panel is used to change between the different tabs of the GUI. Each tab focuses on a different aspect of model creation.

The current tab is highlighted with a grey background.

  1. Component selector: This section shows the components loaded into the GUI. Prefixes denote the component type, and the bracketed value reports the location of the component. Components can be loaded using the ‘Load components’ button (open folder) and created using the ‘New component’ button (blank sheet). Components can be selected for editing here. The background will be one of four colours:

Clicking on the divider to the right of this panel will hide and show the panel.

  1. Component visualisation: This section of the component tab provides an automatically laid out overview of the current component selected for editing. The elements (the building blocks that define behaviour) of the component are displayed visually, and cannot be moved by the user. The display region can be panned by clicking and dragging the background and zoomed using the mousewheel or magnifying glass buttons in the toolbar. The ‘Ports’ and ‘Params, Vars & Alias’ boxes can be hidden or shown using the toggles on the toolbar. Interacting with the component elements will be covered later in this tutorial.

component_editor Fig 2: Overview of the component editor tab

  1. Properties panel: This panel is used to edit the properties of the component elements.

  2. Toolbar: The toolbar gives context-sensitive buttons for interacting with the current component that is being edited. Note: Hover the mouse over the buttons for a tooltip description.

Example - the LIF neuron and synapse

As an example we will create a Leaky Integrate and Fire (LIF) neuron with a conductance-based single exponential decaying synapse using the component editor tab. The LIF is a simple neuron model that provides a great example for demonstrating the tools. Before getting started building the model we first have a look at the equations that govern the LIF and synaptic model behaviour and show how these should be broken down for implementation.

The equation for the membrane voltage dynamics is:

iaf_memb

where v is the membrane potential, tau is the membrane time constant, I_off and I_syn are the offset and synaptic currents acting on the membrane, C_m is the membrane capacitance and v_rest is the resting membrane potential.

When the voltage crosses a threshold it is reset to a fixed value and the neuron enters a fixed refractory period in which there are no dynamics for the membrane.

The equation for the synaptic current is:

iaf_I

where g_syn is the instantaneous synaptic conductance and E_syn is the synaptic reversal potential.

The instantaneous conductance is modelled as follows:

iaf_g

where tau is the synaptic decay constant, s denotes the index in the set of action potential reaching the synapse, and g-bar is the maximal synaptic conductance. The delta function applies an instantaneous rise of g-max to the synaptic conductance each time an action potential acts on the synapse. This equation is more commonly expressed as the solution:

iaf_g_solved

We have tried with the GUI to spare the user as much concern for the implementation of the model as possible to allow them to focus on the model itself, however in mapping these equations to SpineML components we must spare a little bit of thought to implementation.

wu_vs_ps Fig3: Diagram of a projection between two populations of neuron_body (blue circles) showing weight_update (green triangles and postsynapse (red squares)

SpineML has three types of neural components: weight_update, post_synapse, and neuron_body. Together the weight_update and postsynapse components form synapses, however the equations that are placed in each need consideration. Weight_updates are calculated for every connection, while postsynapses are calculated for every target neuron.

The diagram in Fig 3 shows this a bit better - there is a projection between the top population of blue neuron_body components and the bottom blue neuron_body components. Every source neuron_body is connected to every adjacent destination neuron_body. There are seven green weight_updates, but only three red postsynapses - one for each destination neuron_body.

So what does this mean for implementation? Everything that must be done on a per connection basis must be put in the weight_update component, and everything else should go in the postsynapse - as they are fewer in number so will apply less computational overhead. For large densely connected populations unnecessary computation in the weight_update can be quite costly to performance.

For this model this involves breaking the model down as follows:

neuron_body:

iaf_memb

postsynapse:

iaf_I

and

ps_2nd

weight_update:

wu_eqn

where each weight_update i applies each input action potential s for that connection (redefined from earlier), and then the contributions for each weight_update i are summed in the postsynapse. This is possible as exponential decays are linearly separable, so can be composed into a single decay without changing the result.

Sorry about the slight added complexity, but we couldn’t think of a method that would preserve the flexibility and performance we desired and not require this step.

Now that we have the equations that describe the neuron and synapse and have broken them into the correct components, we can begin building!

The IAF neuron_body

Add a new component using the ‘New component’ button at the top of the component selection panel.

First we shall create a SpineML component for the neuron_body. To do that we must add elements to the component and set their properties to create the behaviour we require.

Component properties

props_component Fig 4: Component properties panel

First we configure the properties of the component as a whole. The properties panel should look as shown to the right (Fig 4). If it does not then make sure the cursor icon is selected in the toolbox and click on the background of the visualisation pane. The properties should be set as follows:

Add Regimes

addregime

Now we add Regimes. These contain sets of differential equations that describe the behaviour of the component in one state.

Regimes: These contain the differential equations that describe the dynamics of the component. Regimes can be switched between using Conditionals (see later). {: .info-popout }

There should now be a new Regime named ‘New_Regime_1’ in the visualisation pane which is selected (note the red outline). In the Properties panel there are the properties that can be set for this Regime, which is just the name. Set the name to ‘Integrating’ - note that the visualisation updates to reflect the changes as you type.

The Add Regime button is no longer on the toolbox as we have the Regime selected, so click on the visualisation pane to deselect the Regime and bring it back. Add another Regime and name it ‘Refractory’.

Click on the background of the visualisation pane to return to the Component Properties and set the ‘Initial Regime’ to ‘Integrating’.

Add a Time Derivative

addde

We now need to add a differential equation element (called a Time Derivatives), which will contain the differential equation for the neuron membrane potential, to the Integrating Regime:

depanel Fig 5: Time derivative properties panel

Select the new Time Derivative element by clicking on it. Note the dotted outline denoting selection of a sub-element. The context sensitive Properties panel now shows the properties of the Time Derivative. The drop down box ‘Variable’ is empty and there are no options to select. We need to add a State Variable for voltage, which will show up here.

Add a State Variable

To add the voltage State Variable:

State Variables and Parameters: There are two types of Properties of a component - Parameters and State Variables. Parameters represent numbers that are static during the simulation of a model, while State Variables can change, thus they represent the current state of the system. {: .info-popout }

pars_sv Fig 6: Right: the toolbar buttons for adding Parameters, State Variables and Aliases. Left: The Params, Vars & Alias box

Finish the Time Derivative

Now we are in a position to finish the Time Derivative we added earlier:

Now we add the equation for the membrane potential dynamics. This must be in the form of C-style code (for details see here: section A.1.16 MathInline ). As an example here is the membrane potential equation:

(I_off+I_syn)/C_m+(v_rest-v)/tau_m

where the tau_m has been moved across to the other side of the equation.

Note that there are several names that we use here that represent values that are fixed for the duration of the simulation (I_off, C_m, v_rest, tau_m), or inputs into the component (I_syn), but we have not defined them. Consequently the text background is red - showing that there is an error in the text string. To solve this we must add Parameters for the fixed values, and an Analog Input Port for I_syn.

Your component should now look as below:

stage1_ce

Add the Parameters

We now must add the Parameters for the membrane potential equation

To add a Parameter:

Click to add four Parameters, and name these Parameters ‘I_off’, ‘C_m’, ‘v_rest’ and ‘tau_m’.

The Parameters can be re-ordered by selecting one and using the up and down buttons on the toolbar.

Set the Dimensions of the Parameters to physiological units (current = nA, capacitance = pF, voltage = mV, time = ms).

Add the Analog Input Port

Ports: There are three types of Port which carry different types of data between components - Analog Ports carry continuous variable data every timestep of the simulation; Event Ports are analogous to action potential spikes, and trigger discrete events in components; Impulse Ports are event ports that, along with the discrete event, carry a value which can be set by the source component. {: .info-popout }

Ports describe the communications of the component with other components. We need to add an Analog Input Port for the incoming current from the synapses, I_syn.

To add an Analog Port:

The Port is currently configured as an output, or Send port by the ‘Analog Mode’ drop-down box. We want to reconfigure it so that it is an input Port. there are two types of input port: Receive Ports take input from a single source, and connecting multiple sources is not possible; Reduce Ports take input from multiple sources and combine them using a Reduce Operator. Since our neuron may receive currents from multiple synapses, we require a Reduce Port. To configure the Port see Fig 7:

reduce_props Fig 7: Reduce Port properties panel

The Properties panel should now look as the image to the right.

Now select the Time Derivative, note that the background to the text is white. If this is not the case then recheck the Parameters and Port for errors.

Add output Ports

We now have a component with membrane dynamics and current input, but we are still lacking any way for the component to communicate to the outside world, and to enter the refractory period. First we consider communication.

The IAF neuron_body needs two outputs. One is an Event Port to transmit spikes, and the other an Analog Port to send voltage for use in a conductance-based synapse.

iaf_v_port

To add and configure the Analog Port (Fig 8):

This Port will now transmit the value of the voltage State Variable.

iaf_spike_port Fig 9: Properties for the Event Port

To add and configure the Event Port (Fig 9):

This Port will send Events when triggered, but we still need to add a mechanism to trigger this Port.

Introduction to Transitions

In order to move between Regimes, or perform actions within the component that depend upon conditions being met, we use Transitions.

There are three types of Transition:

For this component we only require OnCondition Transitions. The remaining two Transition types will be described when creating the weight_update component.

All Transition types can contain State Assignments which can update State Variables by assignment - i.e.:

sv1 = sv1+(var1-sv2*exp(var2))

They can also contain EventOut and ImpulseOut which trigger Events and Impulses to be transmitted on set Ports.

Adding an OnCondition for the spike

The three types of Transition are added using the three toolbox buttons below the cursor arrow button (C: OnCondition; E: OnEvent; I: OnImpulse). Clicking these buttons exits the selection mode (denoted by the cursor arrow) and enters a creation mode for the respective Transition. To add an OnCondition Transition (see Fig 10):

added_oc Fig 10: The new OnCondition

Selecting the OnCondition brings three new buttons to the context sensitive toolbar: add State Assignment (=), add EventOut (e^), and add ImpulseOut (I^).

We now need to configure this OnCondition to activate when the neuron reaches a set threshold, then reset the voltage to zero and note the time of the spike (which will be used to return to the ‘Integrating’ Regime after the refractory time period).

Configuring the OnCondition

OnCondition Properties

We now need to apply some of the lessons learned so far to set up the OnCondition, as we need new Parameters for the threshold and reset voltages, a new State Variable to store the spike time, and a new Parameter for the refractory time period:

These can then be used to configure the OnCondition:

v > v_thresh

Your component should now look similar to that is Fig 11.

iaf_screenshot_2 Fig 11: Overview of the component at this stage

Adding a State Assignment

state_ass_props Fig 12: State Assignment Properties

We need two State Assignments in this OnCondition. These will reset the voltage to v_reset and log the spike time in t_spike.

math_t Fig 13: ‘t_spike’ State Assignment Properties

We now configure the second State Assignment with ‘Variable’ set to ‘t_spike’. We wish to store the current simulation time, so we use the reserved variable t to do so, setting ‘Maths’ to ‘t’, as shown to the right (Fig 13).

Adding an EventOut

The final thing that the component must do when the voltage threshold is crossed is emit an action potential, or spike. To do this we add an EventOut to the OnCondition.

Add an OnCondition to return from the Refractory Regime

While in the ‘Refractory’ Regime there are no Time Derivative and the system does not update. We therefore need to add a means of returning to the ‘Integrating’ Regime after the refractory time period has elapsed to allow the component to function again. We do this using another OnCondition Transition.

t > t_spike+t_refrac

Now when t has progressed past t_spike by t_refrac (i.e. the current time is the spike time + the refractory time period) the component will transition back into the ‘Integrating’ Regime.

Storing the finished component for use and saving

The component is now complete. However in order to use it in models we must first validate and store the model. To do this:

or

To save the model to a file use the ‘Save component’ and ‘Save component as…’ items in the ‘File’ menu. The component will be stored as a SpineML xml file. The model does not have to be stored to be saved, but will be validated before saving.

If there are any validation problems with the model these will be reported and the model will not be stored or saved! Only correctly formed models can be stored or saved

iaf_done Fig 14: The finished component

Test the component

We will now quickly run through the steps to test this neuron - note that no explanation will be given here as these are subjects for the remaining tutorials.

You should see regular spiking.

Note: if you play with the parameter and run the experiment again the plot will update with the new results!

The synapse weight_update

It is worth noting that this weight_update component contains no Time Derivatives or Analog Ports. This means that everything in the component acts in response to Events or Impulses - it is Event Driven. By making the weight_update Event Driven the simulation can calculate synapses much faster. You should always aim to make your weight_updates Event Driven for this reason. That said, if it is necessary to add Time Derivatives or Analog Ports then this is possible, however you models will take a significant performance hit for doing so. {: .info-popout }

The synapse weight_update component is simple, but introduces several new component elements - OnEvent Transitions, Impulse Ports and ImpulseOut elements. This section of the tutorial assumes that you have read the previous section.

This component takes a spike from a neuron_body and sends on an Impulse of the connection weight to the postsynapse. It converts an Event into an Event with a value (an Impulse) with the value being the weight of the connection.

Initial setup

Adding the Event Port

wu_event_props Fig 15: Event Port properties

We first add the input to the component, which is an Event Port (this will be connected to the Event Send Port of the neuron_body):

Currently this event port cannot affect the component behaviour, and requires an OnEvent to trigger when an event is received. First, however, it is useful to add the output port for the component - an Impulse Port.

Adding the Impulse Port

wu_imp_props Fig 16: Impulse Port properties

Now we add an Impulse port that will be the output of the component:

This configures the Impulse port to send the value ‘g’ when it is triggered - note that g is a Parameter and thus will not change during the simulation as this synapse cannot learn. To implement a learning synapse g would be a State Variable.

Adding an OnEvent Transition

We must now connect the input to the output, which we do using a type of Transition we have not previously used - an OnEvent. The OnEvent is triggered when an Event arrives on the specified Event Port, and can then execute a set of State assignments, EventOuts and ImpulseOuts. We will use the OnEvent to trigger an ImpulseOut and pass on the weight to the postsynapse component.

onevent_props Fig 17: OnEvent properties

This will be triggered every time the neuron_body connected to the Port ‘spike’ sends an Event.

Adding an ImpulseOut

Now we need to get the OnEvent to send an Impulse.

impout_props Fig 18: ImpulseOut Properties

Now every time the component receives an Event on Port ‘spike’ it will send an Impulse on Port ‘g’. This completes the component! Now validate and store the component and save to hard drive if you wish. Fig 19 shows the finished component.

complete_wu Fig 19: Complete weight_update component

The synapse postsynapse

The final component in this tutorial is the exponential decaying postsynapse. This mainly uses the techniques described in the previous sections, so detail will only be provided for the new elements - the Alias and the OnImpulse.

ps_so_far Fig 20: Postsynapse component after Initial setup

Initial setup

dg/dt = -g/tau_syn

The component should now look like shown to the right (Fig 20).

Note: we have added an input Port g_in, which is not the same name as the output Port g of the weight_update. This is not an issue as the Network Editor will allow differently named Ports to be joined as long as the type and dimensions (if Analog) are consistent, or if Analog one of the Analog Ports has no dimensionality.

Note2: The ‘v’ Port that we have added will need to be connected to the postsynapsic neuron body, this will be covered in the Network Editor tutorial.

Now we will investigate adding an Alias.

Adding an Alias

Aliases are used for two purposes. One is to reduce the length of mathematical expressions by allowing the use of variables which represent a value derived in a separate equation:

dv/dt = I_sum/C_m - v; I_sum = I_off + I_syn + I_noise

Here I_sum is an Alias used to simplify the differential equation.

The second use of Aliases is to derive values for Analog or Impulse Port output. An Analog or Impulse Port may wish to transmit a value derived from the Inputs, Parameters, State Variables and other Aliases of the component. In this case the output I_syn uses the voltage from the neuron_body and the reversal potential (E) to transform the conductance of the synapse into a current.

alias_props Fig 21: Alias Properties

To add the Alias for I_syn:

g*(E-v)

Add an OnImpulse Transition

Now the final step in creating this component is to take input from the Impulse Port. To do so we need to use an OnImpulse Transition, the remaining Transition type that we have not used so far.

sa_props_imp Fig 22: State Assignment Properties

To add an OnImpulse:

g+g_in

The component is now finished! validate and store and then save to hard drive if you wish. See Fig 23 for the finished component.

ps_done Fig 23: Finished postsynapse component

Final remarks

I hope this tutorial has introduced you to how to create components in the GUI. Finished versions of the components described here can be found below, and the second tutorial will instruct you how to combine components into networks consisting of neural populations connected by projections. This will also allow you to build networks to test out the components you have just created!

Finished components for reference

Note to selves: ADD FINISHED COMPONENTS HERE