Spiking Neuron Network Simulator  1.0
Simulation and training of spiking neuron networks, primarily theta neurons
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Pages
SpikingNeuronNetwork.cs
Go to the documentation of this file.
1 namespace SpikingNeuronNetwork.Lib
2 {
3  using Interfaces;
4  using NeuronModels;
5  using System;
6  using System.Collections.Generic;
7  using System.Linq;
8  using Training;
9 
13  public class SpikingNeuronNetwork
14  {
18  public int Id { get; set; }
19 
23  public IList<int> NumNeuronsPerLayer { get; private set; }
24 
28  public int NumInputs
29  {
30  get { return _weightMatrix.Cols - NumNeuronsPerLayer.Sum(); }
31  }
32 
36  public bool IsLayered
37  {
38  get { return _isLayered; }
39  }
40 
41  private Dictionary<int, SpikingNeuron> _neurons;
42  private const double Epsilon = 1e-10;
43  private readonly Matrix _weightMatrix; //Should supercede the connections in the neuron objects
44  private readonly Random _random = new Random();
45  private readonly bool _isLayered;
46 
55  public SpikingNeuronNetwork(int numInputs, int numNeurons, Type neuronType, double weightIni = 1e6, Dictionary<string, string> neuronProperties = null)
56  {
57  // Create Network with Random Topology and Weights
58  NumNeuronsPerLayer = new List<int> {numNeurons};
59  _isLayered = numNeurons == 1;
60  _weightMatrix = weightIni > 100 ?
61  Matrix.RandomMatrix(numInputs + numNeurons, numInputs + numNeurons, 1.0, 0.5) :
62  Matrix.ConstantMatrix(numInputs + numNeurons, numInputs + numNeurons, weightIni);
63 
64  // Selfloops are disallowed is there no synaptic delays
65  // an output spike could cause artificial recursion
66  for (var i = 0; i < _weightMatrix.Cols; i++)
67  {
68  _weightMatrix[i, i] = 0;
69  }
70 
71  //Input neuron or neuron to input neuron connections are disallowed
72  for (var i = 0; i < numInputs; i++)
73  for (var n = 0; n < _weightMatrix.Rows; n++)
74  _weightMatrix[n, i] = 0;
75 
76  AssignNeurons(numInputs, numNeurons, neuronType, neuronProperties);
77  }
78 
87  public SpikingNeuronNetwork(int numInputs, IList<int> numNeuronsPerLayer, Type neuronType, double weightIni = 1e6, Dictionary<string, string> neuronProperties = null)
88  {
89  NumNeuronsPerLayer = numNeuronsPerLayer;
90  // Create a layered weight matrix
91  var numNeurons = numNeuronsPerLayer.Sum();
92  var totalNeurons = numInputs + numNeurons;
93  _isLayered = true;
94  _weightMatrix = Matrix.ZeroMatrix(totalNeurons, totalNeurons);
95 
96  const double probability = 0.5;
97  const int dispersion = 1;
98  // Input layer
99  for (var j = 0; j < numInputs; j++)
100  {
101  for (var k = numInputs; k < numInputs + numNeuronsPerLayer[0]; k++)
102  {
103  var newWeight = weightIni > 100 ? ((_random.NextDouble() <= probability) ? dispersion * 2 * (_random.NextDouble() - 0.5) : 0) : weightIni;
104  _weightMatrix[j, k] = newWeight;
105  }
106  }
107  // Hidden and output layers
108  for (var layer = 1; layer < numNeuronsPerLayer.Count; layer++)
109  {
110  for (var j = numInputs; j < numInputs + numNeuronsPerLayer.Take(layer).Sum(); j++)
111  {
112  for (var k = numInputs + numNeuronsPerLayer.Take(layer).Sum();
113  k < numInputs + numNeuronsPerLayer.Take(layer + 1).Sum();
114  k++)
115  {
116  var newWeight = weightIni > 100
117  ? ((_random.NextDouble() <= probability)
118  ? dispersion * 2 * (_random.NextDouble() - 0.5)
119  : 0)
120  : weightIni;
121  _weightMatrix[j, k] = newWeight;
122  }
123  }
124  }
125 
126  // Selfloops are disallowed without synaptic delays because
127  // an output spike could cause artificial recursion
128  for (var i = 0; i < _weightMatrix.Cols; i++)
129  {
130  _weightMatrix[i, i] = 0;
131  }
132 
133  //Input neuron or neuron to input neuron connections are disallowed
134  for (var i = 0; i < numInputs; i++)
135  for (var n = 0; n < _weightMatrix.Rows; n++)
136  _weightMatrix[n, i] = 0;
137 
138  AssignNeurons(numInputs, numNeurons, neuronType, neuronProperties);
139  }
140 
149  private SpikingNeuronNetwork(Random random, Matrix weightMatrix, bool isLayered, IList<int> numNeuronsPerLayer, Dictionary<int, SpikingNeuron> neurons)
150  {
151  _random = random;
152  _weightMatrix = weightMatrix;
153  _isLayered = isLayered;
154  _neurons = neurons;
155  NumNeuronsPerLayer = numNeuronsPerLayer;
156  }
157 
163  {
164  var newNeurons = _neurons.ToDictionary(kvp => kvp.Key, kvp => (SpikingNeuron) kvp.Value.Clone());
165  var clonedNetwork = new SpikingNeuronNetwork(_random, _weightMatrix, _isLayered, NumNeuronsPerLayer, newNeurons);
166  foreach (var neuronIndices in newNeurons.Keys)
167  {
168  newNeurons[neuronIndices].NeuronNetwork = clonedNetwork;
169  }
170  return clonedNetwork;
171  }
172 
178  public ISpikingNeuron GetStandAloneNeuron(int neuronIndex)
179  {
180  return _neurons[neuronIndex].Clone();
181  }
182 
189  public List<double> CalculatePostSpikeDerivatives(NeuronFiringHistory neuronFiringHistory)
190  {
191  return _neurons[neuronFiringHistory.NeuronIndex].CalculatePostSpikeDerivatives(neuronFiringHistory);
192  }
193 
200  public List<double> CalculatePostSpikeDerivativeNumerical(NeuronFiringHistory neuronFiringHistory)
201  {
202  return _neurons[neuronFiringHistory.NeuronIndex].CalculatePostSpikeDerivativesNumerical(neuronFiringHistory);
203  }
204 
211  public Dictionary<Synapse, NeuronDerivativeParameters> CalculateOutputSpikeTimeDerivatives(NeuronFiringHistory neuronFiringHistory)
212  {
213  return _neurons[neuronFiringHistory.NeuronIndex].CalculateOutputSpikeTimeDerivatives(neuronFiringHistory);
214  }
215 
222  public Dictionary<Synapse, NeuronDerivativeParameters> CalculateOutputSpikeTimeDerivativesNumerical(NeuronFiringHistory neuronFiringHistory)
223  {
224  return _neurons[neuronFiringHistory.NeuronIndex].CalculateOutputSpikeTimeDerivativesNumerical(neuronFiringHistory);
225  }
226 
232  public Dictionary<int, NeuronFiringHistory> RunSpikingNeuronNetwork(List<Spike> inputSpikes)
233  {
234  // Create a priority queue of NextSpikeTimes for each Neuron
235  double currentTime = 0;
236  const double endTime = 500;
237  var neuronFiringHistories = new Dictionary<int, NeuronFiringHistory>();
238  var spikeQueue = new SpikePriorityQueue();
239  spikeQueue.AddRange(inputSpikes);
240  foreach (var neuron in _neurons)
241  {
242  spikeQueue.Add(new Spike { Time = neuron.Value.NextSpikeTime, NeuronIndex = neuron.Value.NeuronIndex});
243  }
244 
245  // Repeat until steady-state or for a fixed time
246  while (currentTime < endTime && spikeQueue.Count > 0)
247  {
248  // Dequeue the next spike to process, eminating from Neuron A and add it to the outputspike List
249  var currentSpike = spikeQueue.Dequeue();
250  currentTime = currentSpike.Time;
251 
252  // Update Neuron A's State and add a new NextSpikeTime to the queue for Neuron A
253  // but skip this step if A is an input neuron
254  if (currentSpike.NeuronIndex > NumInputs - 1)
255  {
256  if (neuronFiringHistories.ContainsKey(currentSpike.NeuronIndex))
257  {
258  neuronFiringHistories[currentSpike.NeuronIndex].OutputSpikes.Add(currentSpike);
259  }
260  else
261  {
262  neuronFiringHistories.Add(currentSpike.NeuronIndex, new NeuronFiringHistory
263  {
264  NeuronIndex = currentSpike.NeuronIndex,
265  OutputSpikes = new List<Spike> { currentSpike },
266  SpikeStats = new SortedSet<SpikeStats>()
267  });
268  }
269  _neurons[currentSpike.NeuronIndex].State.Time = currentSpike.Time;
270  _neurons[currentSpike.NeuronIndex].State.StateVariable = _neurons[currentSpike.NeuronIndex].PostSpikeState;
271  }
272 
273  // Get the set of neurons connected to Neuron A's output (Neurons B)
274  var outputNeurons = GetNeuronsOutputNeurons(currentSpike.NeuronIndex);
275 
276  // Foreach neuron in B, update the State and update the NextSpikeTime in the queue
277  foreach (var outputNeuron in outputNeurons)
278  {
279  var oldSpike = new Spike {Time = outputNeuron.NextSpikeTime, NeuronIndex = outputNeuron.NeuronIndex};
280  SpikeStats currentSpikeStats;
281  var outputSpikeList = outputNeuron.ProcessSpike(currentSpike, out currentSpikeStats);
282  if (neuronFiringHistories.ContainsKey(outputNeuron.NeuronIndex))
283  {
284  neuronFiringHistories[outputNeuron.NeuronIndex].OutputSpikes.AddRange(outputSpikeList);
285  neuronFiringHistories[outputNeuron.NeuronIndex].SpikeStats.Add(currentSpikeStats);
286  }
287  else
288  {
289  neuronFiringHistories.Add(outputNeuron.NeuronIndex, new NeuronFiringHistory
290  {
291  NeuronIndex = outputNeuron.NeuronIndex,
292  OutputSpikes = new List<Spike>(outputSpikeList),
293  SpikeStats = currentSpikeStats == null ? new SortedSet<SpikeStats>() : new SortedSet<SpikeStats> {currentSpikeStats}
294  });
295  }
296 
297  // Find the outputNeuron in the spikeQueue and update the nextSpikeTime
298  spikeQueue.Update(oldSpike, outputNeuron.NextSpikeTime);
299  }
300  }
301 
302  return neuronFiringHistories;
303  }
304 
308  public void ResetNetwork()
309  {
310  foreach (var neuron in _neurons)
311  {
312  neuron.Value.ResetState();
313  }
314  }
315 
321  public List<Synapse> GetNeuronsInputSynapses(int neuronIndex)
322  {
323  return _neurons[neuronIndex].Weights.Select(x => x.Key).ToList();
324  }
325 
331  public List<Synapse> GetNeuronsOutputSynapses(int neuronIndex)
332  {
333  var weights = _weightMatrix.GetRow(neuronIndex);
334  var outputNeuronIndices = weights.FindAllIndexOfNot(0);
335  return outputNeuronIndices
336  .Select(outputNeuronIndex => new Synapse(neuronIndex, outputNeuronIndex))
337  .ToList();
338  }
339 
345  public List<int> GetNeuronIndicesByLayer(int layerIndex)
346  {
347  if (!IsLayered || layerIndex >= NumNeuronsPerLayer.Count)
348  {
349  return null;
350  }
351  var startIndex = NumInputs + NumNeuronsPerLayer.Where((x,i) => i < layerIndex).Sum();
352  return Enumerable.Range(startIndex, NumNeuronsPerLayer[layerIndex]).ToList();
353  }
354 
359  public List<int> GetOutputLayerNeuronIndices()
360  {
361  return GetNeuronIndicesByLayer(NumNeuronsPerLayer.Count - 1);
362  }
363 
369  internal double GetSynapticWeight(Synapse synapse)
370  {
371  if (synapse.InputNeuronIndex > _weightMatrix.Rows) throw new ArgumentException("Invalid Synapse: " + synapse);
372  if (synapse.OutputNeuronIndex > _weightMatrix.Cols) throw new ArgumentException("Invalid Synapse: " + synapse);
373  return _weightMatrix[synapse.InputNeuronIndex, synapse.OutputNeuronIndex];
374  }
375 
381  internal Dictionary<Synapse, double> GetNeuronWeights(int neuronIndex)
382  {
383  if (neuronIndex > _weightMatrix.Cols) return null;
384  var weights = new Dictionary<Synapse, double>();
385  var col = _weightMatrix.GetCol(neuronIndex); // Want Column neuronIndex for connections TO neuron
386  for (var j = 0; j < col.Rows; j++)
387  {
388  if (Math.Abs(col[j, 0]) > Epsilon)
389  {
390  weights.Add(new Synapse(j, neuronIndex), col[j, 0]);
391  }
392  }
393  return weights;
394  }
395 
401  internal void SetSynapticWeight(Synapse synapse, double newValue)
402  {
403  if (synapse.InputNeuronIndex > _weightMatrix.Rows || synapse.OutputNeuronIndex > _weightMatrix.Cols) return;
404  _weightMatrix[synapse.InputNeuronIndex, synapse.OutputNeuronIndex] = newValue;
405  }
406 
412  internal List<SpikingNeuron> GetNeuronsOutputNeurons(int neuronIndex)
413  {
414  var weights = _weightMatrix.GetRow(neuronIndex);
415  var outputNeuronIndices = weights.FindAllIndexOfNot(0);
416  return outputNeuronIndices.Where(_neurons.ContainsKey)
417  .Select(x => _neurons[x])
418  .ToList();
419  }
420 
428  private void AssignNeurons(int numInputs, int numNeurons, Type neuronType, Dictionary<string,string> neuronProperties = null)
429  {
430  _neurons = new Dictionary<int, SpikingNeuron>();
431  if (!typeof(ISpikingNeuron).IsAssignableFrom(neuronType))
432  {
433  throw new ArgumentException("Neuron type " + neuronType.Name + " must derive from ISpikingNeuron");
434  }
435 
436  var neuronConstructor = neuronType.GetConstructor(new Type[] { });
437  if (neuronConstructor == null)
438  {
439  throw new ArgumentException("Neuron type " + neuronType.Name + " must implement an empty constructor");
440  }
441  foreach (var neuronIndex in Enumerable.Range(numInputs, numNeurons))
442  {
443  _neurons.Add(neuronIndex, (SpikingNeuron)neuronConstructor.Invoke(new object[] { }));
444  _neurons[neuronIndex].NeuronNetwork = this;
445  _neurons[neuronIndex].NeuronIndex = neuronIndex;
446  var typeProperties = neuronType.GetProperties();
447  foreach (var neuronProperty in neuronProperties ?? new Dictionary<string, string>())
448  {
449  var typeProperty = typeProperties.First(x => x.Name == neuronProperty.Key);
450  if (typeProperty == null || !typeProperty.CanWrite) continue;
451  var propType = typeProperty.PropertyType;
452  object castedValue;
453  if (propType.BaseType != null && propType.BaseType.Name == "Enum")
454  {
455  castedValue = Enum.Parse(propType, neuronProperty.Value);
456  }
457  else
458  {
459  castedValue = Convert.ChangeType(neuronProperty.Value, propType);
460  }
461 
462  typeProperty.SetValue(_neurons[neuronIndex], castedValue);
463  }
464  _neurons[neuronIndex].ResetState();
465  }
466  }
467  }
468 }
SpikingNeuronNetwork(int numInputs, IList< int > numNeuronsPerLayer, Type neuronType, double weightIni=1e6, Dictionary< string, string > neuronProperties=null)
Creates a Spiking Neuron Network Object with a Layered Topology
List< double > CalculatePostSpikeDerivatives(NeuronFiringHistory neuronFiringHistory)
Calculates Post Spike Derivatives for a specific neuron, that is the derivate of the state after the ...
void ResetNetwork()
Resets the network to its initial state
List< Synapse > GetNeuronsOutputSynapses(int neuronIndex)
Get a list of output synapses for a specific neuron
SpikingNeuronNetwork Clone()
Creates a deep copy of the spiking neuron network
SpikingNeuronNetwork(int numInputs, int numNeurons, Type neuronType, double weightIni=1e6, Dictionary< string, string > neuronProperties=null)
Creates a Spiking Neuron Network Object With Random Connectivity Topology
Spiking Neuron Abstract Class, Inherits From ISpikingNeuron
Dictionary< Synapse, NeuronDerivativeParameters > CalculateOutputSpikeTimeDerivatives(NeuronFiringHistory neuronFiringHistory)
Calculates Output Spike Derivatives for a specific neuron, that is the derivate of the output spike t...
List< int > GetOutputLayerNeuronIndices()
Gets a list of output layer neuron indices
Spike Priority Queue used for determining what spike to process next based on spike timing ...
Dictionary< int, NeuronFiringHistory > RunSpikingNeuronNetwork(List< Spike > inputSpikes)
Runs the spiking neuron network, processing the effect of all input spikes
List< int > GetNeuronIndicesByLayer(int layerIndex)
Gets a list of neuron indices by layer
int InputNeuronIndex
Gets or sets the index of the input neuron.
Definition: Synapse.cs:14
The spike statistics class
Definition: SpikeStats.cs:9
ISpikingNeuron GetStandAloneNeuron(int neuronIndex)
Gets the stand alone neuron.
List< double > CalculatePostSpikeDerivativeNumerical(NeuronFiringHistory neuronFiringHistory)
Calculates Post Spike Derivatives numerically for a specific neuron, that is the derivate of the stat...
List< Synapse > GetNeuronsInputSynapses(int neuronIndex)
Get a list of input synapses for a specific neuron
Dictionary< Synapse, NeuronDerivativeParameters > CalculateOutputSpikeTimeDerivativesNumerical(NeuronFiringHistory neuronFiringHistory)
Calculates Output Spike Derivatives numerically for a specific neuron, that is the derivate of the ou...
int OutputNeuronIndex
Gets or sets the index of the output neuron.
Definition: Synapse.cs:22