Creating a simulation requires a few steps:
See the completed model file here: SimpleExample.cpp
Let's step through the above steps. These commands require that you have included the Spike header. To see a complete version of this example, visit:
Step one is the instantiation of the Model class. You can use this as an opportunity to also set the time-step you wish for you simulation (default; 0.1ms).
SpikingModel * ExampleModel = new SpikingModel();
ExampleModel->SetTimestep(0.00001); // units: seconds
Adding components (Neuron class, Synapse class, and Input class) are the next step. To see the available options for these, see the Model Components page.
In this example, the network will be built with LIF Neurons, Conductance Based Synapses, and Generator Input Neurons.
Instantiating the LIF Neuron and Conductance Synapses classes:
LIFSpikingNeurons * lif_spiking_neurons = new LIFSpikingNeurons();
ConductanceSpikingSynapses * conductance_spiking_synapses = new ConductanceSpikingSynapses();
GeneratorInputSpikingNeurons* generator_input_neurons = new GeneratorInputSpikingNeurons();
Setting these in the model:
ExampleModel->input_spiking_neurons = generator_input_neurons;
ExampleModel->spiking_neurons = lif_spiking_neurons;
ExampleModel->spiking_synapses = conductance_spiking_synapses;
We can also add an Activity Monitor to collect the LIF neuron spike times:
SpikingActivityMonitor* spike_monitor = new SpikingActivityMonitor(lif_spiking_neurons);
ExampleModel->AddActivityMonitor(spike_monitor);
Note that the activity monitor requires a pointer to the neuron population it should monitor in it's constructor call.
Generator Input Neurons
To add populations of neurons or to create synaptic connections, you must set the parameters you wish for in the corresponding parameter structures. After filling these parameters, you can then use this parameter structure to create neurons/synapses.
First creating 10 generator input neurons. Note that for all neuron groups, the group shape is two-dimensional (makes 2D topological connectivity easier).
Step one, create the parameter structure outlining the population you wish to add to the simulation:
generator_input_spiking_neuron_parameters_struct* input_neuron_params = new generator_input_spiking_neuron_parameters_struct();
input_neuron_params->group_shape[0] = 1; // x-dimension of the input neuron layer
input_neuron_params->group_shape[1] = 10; // y-dimension of the input neuron layer
Step two, Add this population to the model:
int input_layer_ID = ExampleModel->AddInputNeuronGroup(input_neuron_params);
Note: when you create a neuron group, an integer ID is returned which you can use to connect these populations later.
LIF Neurons
// SETTING UP NEURON GROUPS
// Creating an LIF parameter structure for an excitatory neuron population and an inhibitory population
// 1 x 100 Layer
lif_spiking_neuron_parameters_struct * excitatory_population_params = new lif_spiking_neuron_parameters_struct();
excitatory_population_params->group_shape[0] = 1;
excitatory_population_params->group_shape[1] = 100;
excitatory_population_params->resting_potential_v0 = -0.074f;
excitatory_population_params->threshold_for_action_potential_spike = -0.053f;
excitatory_population_params->somatic_capacitance_Cm = 500.0*pow(10, -12);
excitatory_population_params->somatic_leakage_conductance_g0 = 25.0*pow(10, -9);
int excitatory_neuron_layer_ID = ExampleModel->AddNeuronGroup(excitatory_population_params);
Different neuron groups can be set up with different parameters, e.g. if your inhibitory neurons require a different parameter set:
lif_spiking_neuron_parameters_struct * inhibitory_population_params = new lif_spiking_neuron_parameters_struct();
inhibitory_population_params->group_shape[0] = 1;
inhibitory_population_params->group_shape[1] = 100;
inhibitory_population_params->resting_potential_v0 = -0.082f;
inhibitory_population_params->threshold_for_action_potential_spike = -0.053f;
inhibitory_population_params->somatic_capacitance_Cm = 214.0*pow(10, -12);
inhibitory_population_params->somatic_leakage_conductance_g0 = 18.0*pow(10, -9);
int inhibitory_neuron_layer_ID = ExampleModel->AddNeuronGroup(inhibitory_population_params);
Synapses
As with the other components, synapses also have parameter structures which are used to set the connectivity parameters:
conductance_spiking_synapse_parameters_struct* input_to_excitatory_parameters = new conductance_spiking_synapse_parameters_struct();
input_to_excitatory_parameters->weight_range[0] = 0.5f;
input_to_excitatory_parameters->weight_range[1] = 10.0f;
input_to_excitatory_parameters->weight_scaling_constant = excitatory_population_params->somatic_leakage_conductance_g0;
input_to_excitatory_parameters->delay_range[0] = timestep;
input_to_excitatory_parameters->delay_range[1] = 5*timestep;
input_to_excitatory_parameters->connectivity_type = CONNECTIVITY_TYPE_ALL_TO_ALL;
Note: if you wish to set weights or delays in this manner, they are created in a uniform distribution between the value at index 0 and index 1 of the range. If you wish to supply alternatively distributed weights or delays, it is recommended that you use the CONNECTIVITY_TYPE_PAIRWISE style.
An example of using the PAIRWISE connectivity style from .mat files or using some percent connectivity is shown in the UtilityFunctions.hpp file.
After a synaptic parameter structure has been created and filled, two populations can be connected:
ExampleModel->AddSynapseGroup(input_layer_ID, excitatory_neuron_layer_ID, input_to_excitatory_parameters);
The first argument is the layer ID of the pre-synaptic population, the second is the layer ID of the post-synaptic population, and the final parameter is the parameter set for the synapses to be created.
Synaptic connections can also be created between the inhibitory and excitatory populations. See the final SimpleExample.cpp file for these.
In the case of the input neurons, we can add stimuli and choose which stimulus is presented during each run of the simulator. An example of adding an input stimulus with specific spike times of specific input neurons is shown below.
// We can now assign a set of spike times to neurons in the input layer
int num_spikes = 5;
int neuron_ids[5] = {0, 1, 3, 6, 7};
float spike_times[5] = {0.1f, 0.3f, 0.2f, 0.5f, 0.9f};
// Adding this stimulus to the input neurons
int first_stimulus = generator_input_neurons->add_stimulus(num_spikes, neuron_ids, spike_times);
To select a specific stimulus for presentation:
generator_input_neurons->select_stimulus(first_stimulus);
Stimuli have IDs which are ordered sequentially with the order they were addeds
Finally, to run the simulation for 5 seconds, we can execute the method from the SpikingModel class:
ExampleModel->run(5.0f);
After this has run, we have some options:
ExampleModel->reset_state();
ExampleModel->run(5.0f);
generator_input_neurons->select_stimulus(second_stimulus);
ExampleModel->run(5.0f);
spike_monitor->save_spikes_as_txt("./");
//spike_monitor->save_spikes_as_binary("./"); // Or as a binary file
ExampleModel->spiking_synapses->save_connectivity_as_txt("./");
// ExampleModel->spiking_synapses->save_connectivity_as_binary("./");
The simulation can be run many times in a single script. Other than changing the input stimulus, it is not recommended that you change the model between these runs.