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
ThetaNeuron.cs
Go to the documentation of this file.
1 namespace SpikingNeuronNetwork.Lib.NeuronModels
2 {
3  using Interfaces;
4  using System;
5  using System.Collections.Generic;
6  using System.Linq;
7  using System.Text;
8  using Training;
9 
13  public class ThetaNeuron : SpikingNeuron
14  {
18  public const double DefaultIo = -0.001;
19 
23  public static double Alpha = 1;
24 
28  public static double Offset = 0.0001;
29 
33  public double Io { get; set; }
34 
35  #region Readonly Properties
36 
40  public override double PostSpikeState
41  {
42  get { return -Math.PI; }
43  }
44 
48  public override double SpikeThreshold
49  {
50  get { return Math.PI; }
51  }
52 
56  public override double InitialState
57  {
58  get { return (Io < 0) ? PositiveFixedPoint + Offset : 0; }
59  }
60 
64  public override double RemainingTimeToSpike
65  {
66  get
67  {
68  if (Io > 0)
69  {
70  return (1 / Beta) * ((SpikeThreshold / 2) - Math.Atan(C1));
71  }
72  if (State.StateVariable > PositiveFixedPoint && State.StateVariable < SpikeThreshold)
73  {
74  return (1 / Beta) * Atanh(1 / C1);
75  }
76  return Double.PositiveInfinity;
77  }
78  }
79 
83  public override OperatingRegion OperatingRegion
84  {
85  get
86  {
87  if (Io > 0)
88  {
89  return OperatingRegion.TonicSpiking;
90  }
91  if (State.StateVariable < PositiveFixedPoint && State.StateVariable > -PositiveFixedPoint)
92  {
93  return OperatingRegion.Quiescent;
94  }
95  if (State.StateVariable >= PositiveFixedPoint)
96  {
97  return OperatingRegion.SpikeGeneration;
98  }
99  return OperatingRegion.Refractory;
100  }
101  }
102 
106  public override double MaxNumericalIntegrationIterations
107  {
108  get
109  {
110  double simulationLimit;
111  // Integrate long enough to include effects of all input spikes
112  if (SpikeQueue.Count > 0)
113  {
114  simulationLimit = (SpikeQueue.Last().Time + 1.5*(Io < 0 ? BaselineFiringTime : RemainingTimeToSpike));
115  }
116  else
117  {
118  simulationLimit = 1.1*BaselineFiringTime;
119  }
120  return simulationLimit;
121  }
122  }
123 
127  public double Period
128  {
129  get { return Io > 0 ? SpikeThreshold/Beta : Double.NaN; }
130  }
131 
135  public double Beta
136  {
137  get { return Math.Sqrt(Math.Abs(Alpha*Io)); }
138  }
139 
143  public double PositiveFixedPoint
144  {
145  get { return (Io < 0) ? Math.Acos((1 + Alpha*Io)/(1 - Alpha*Io)) : Double.NaN; }
146  }
147 
151  public double BaselineFiringTime
152  {
153  get { return (1/Beta)*Atanh(Beta/Math.Tan((InitialState)/2)); }
154  }
155 
159  private double C1
160  {
161  get { return Math.Tan(State.StateVariable/2)/Beta; }
162  }
163 
164  #endregion
165 
171  private static double Atanh(double x)
172  {
173  return (0.5 * Math.Log((1.0 + x) / (1.0 - x)));
174  }
175 
179  public ThetaNeuron()
180  : this(0)
181  {
182  }
183 
190  public ThetaNeuron(SpikingNeuronNetwork network, int neuronIndex, double newIo = DefaultIo)
191  : base(network, neuronIndex)
192  {
193  Io = newIo;
194  ResetState();
195  }
196 
202  public ThetaNeuron(IReadOnlyList<double> weights, double newIo = DefaultIo)
203  : base(weights)
204  {
205  Io = newIo;
206  ResetState();
207  }
208 
215  public ThetaNeuron(int numInputs = 3, double weightIni = 1e6, double newIo = DefaultIo)
216  : base(numInputs, weightIni)
217  {
218  Io = newIo;
219  ResetState();
220  }
221 
227  public override double StateDerivative(double phase)
228  {
229  return ((1 - Math.Cos(phase)) + Alpha*Io*(1 + Math.Cos(phase)));
230  }
231 
236  public override ISpikingNeuron Clone()
237  {
238  return new ThetaNeuron(Weights.Select(x => x.Value).ToList())
239  {
240  NeuronIndex = NeuronIndex,
241  NeuronNetwork = null,
242  State = State,
243  Method = Method,
244  Io = Io,
245  Verbose = Verbose,
246  };
247  }
248 
252  public override string ToString()
253  {
254  var neuronStringBuilder = new StringBuilder();
255  neuronStringBuilder.AppendLine("\nNeuron Properties: ");
256  neuronStringBuilder.AppendLine("\tBeta: " + String.Format("{0:0.0000}", Beta));
257  neuronStringBuilder.AppendLine("\tIo: " + Io);
258  neuronStringBuilder.AppendLine("\tPositive Fixed Point: " + String.Format("{0:0.0000}", PositiveFixedPoint));
259  neuronStringBuilder.AppendLine("\tPhase: " + String.Format("{0:0.0000}", State.StateVariable));
260  neuronStringBuilder.AppendLine("\tTime: " + String.Format("{0:0.0000}", State.Time));
261  neuronStringBuilder.AppendLine("\tNumber of Inputs: " + NumInputs);
262  neuronStringBuilder.AppendLine("\tWeights: ");
263  neuronStringBuilder.Append("\t\t");
264  neuronStringBuilder.AppendLine(GetWeightsString(Weights));
265  neuronStringBuilder.AppendLine();
266  neuronStringBuilder.AppendLine();
267  return neuronStringBuilder.ToString();
268  }
269 
277  public override List<Spike> RunThetaNeuron(List<Spike> inputSpikes, int maxNumOutputSpikes = MaxNumOutputSpikes)
278  {
279  SpikeQueue = SpikePriorityQueue.CreateSpikeQueue(inputSpikes, State.Time);
280  var outputSpikes = new List<Spike>();
281  if (SpikeQueue.Count == 0)
282  {
283  return outputSpikes;
284  }
285 
286  if (Verbose)
287  {
288  Console.WriteLine("Input Spike Times: ");
289  Console.Write(SpikeSet.GetSpikeTimesString(inputSpikes));
290  Console.WriteLine("Baseline Firing Time: " + BaselineFiringTime);
291  Console.WriteLine("Simulation Method: " + Method);
292  Console.WriteLine();
293  }
294 
295  switch (Method)
296  {
297  case SimulationMethod.EventDriven:
298  {
299  while (SpikeQueue.Count > 0)
300  {
301  SpikeStats spikeStats;
302  var newOutputSpikeTimes = ProcessSpike(SpikeQueue.Dequeue(), out spikeStats);
303  if (newOutputSpikeTimes != null)
304  {
305  outputSpikes.AddRange(newOutputSpikeTimes);
306  }
307 
308  if (outputSpikes.Count>=maxNumOutputSpikes)
309  {
310  return outputSpikes;
311  }
312  }
313 
314  //Calculate thm later to save time, unless needed to spike checking
315 
316  //If Io<0 and phase is between the positive fixed point and pi, calculate the final ts
317  //with or without previous input spikes
318  // If Io>0, produce an addition output spike if an input spike has occured since the
319  // last output spike or if no output spikes have been produced yet
320  if ((Io < 0 && State.StateVariable > PositiveFixedPoint) || (Io > 0 && (outputSpikes.Count == 0 || State.Time > outputSpikes.Last().Time)))
321  {
322  outputSpikes.Add(new Spike { NeuronIndex = NeuronIndex, Time = NextSpikeTime });
323  if (Verbose)
324  {
325  Console.WriteLine("---> " + outputSpikes.Last());
326  }
327  }
328  }
329  break;
330 
331  case SimulationMethod.Numerical:
332  {
333  outputSpikes = RunThetaNeuronNumerically(maxNumOutputSpikes);
334  }
335  break;
336  }
337 
338  if (Verbose)
339  {
340  Console.WriteLine();
341  }
342 
343  return outputSpikes;
344  }
345 
352  public override List<double> CalculatePostSpikeDerivatives(NeuronFiringHistory neuronFiringHistory)
353  {
354  var postSpikeDerivativeProducts = new List<double>();
355  SpikeStats previousSpikeStat = null;
356  foreach (var spikeStat in neuronFiringHistory.SpikeStats)
357  {
358  if (previousSpikeStat == null)
359  {
360  previousSpikeStat = spikeStat;
361  continue;
362  }
363 
364  postSpikeDerivativeProducts.Add(((1 + Math.Cos(spikeStat.PostSpikeState.StateVariable)) *
365  (Math.Pow(Math.Tan(spikeStat.PreSpikeState.StateVariable / 2), 2) + Alpha * Io)) /
366  StateDerivative(previousSpikeStat.PostSpikeState.StateVariable));
367  previousSpikeStat = spikeStat;
368  }
369 
370  return postSpikeDerivativeProducts;
371  }
372 
379  public override Dictionary<Synapse, NeuronDerivativeParameters> CalculateOutputSpikeTimeDerivatives(NeuronFiringHistory neuronFiringHistory)
380  {
381  var nHatPostSpikeState = Double.NaN;
382  var nHatPreSpikeState = Double.NaN;
383  var nHatWeight = Double.NaN;
384  var spikesProcessed = 0;
385  var inputSynapses = Weights.Select(x => x.Key).ToList();
386 
387  var outputSpikeTimeDerivatives = inputSynapses.ToDictionary(synapse => synapse, synapse => new NeuronDerivativeParameters
388  {
389  OutputSpikeTimeToInputSpikeTimeDerivative = 0, OutputSpikeTimeToWeightDerivative = 0
390  });
391 
392  if (neuronFiringHistory.OutputSpikes.Count == 0)
393  {
394  return outputSpikeTimeDerivatives;
395  }
396 
397  var actualFiringTime = neuronFiringHistory.OutputSpikes.First().Time;
398  foreach (var spikeStat in neuronFiringHistory.SpikeStats.TakeWhile(spikeStat => spikeStat.PreSpikeState.Time < actualFiringTime))
399  {
400  nHatPreSpikeState = spikeStat.PreSpikeState.StateVariable;
401  nHatPostSpikeState = spikeStat.PostSpikeState.StateVariable;
402  nHatWeight = spikeStat.Weight;
403  spikesProcessed++;
404  }
405  var nHat = spikesProcessed - 1;
406 
407  // No input spikes occur before the output spike, so little adjustments to weights will have no effect
408  if (spikesProcessed == 0)
409  {
410  return outputSpikeTimeDerivatives;
411  }
412 
413  // Simple Version when there is only one input spike
414  if (neuronFiringHistory.SpikeStats.Count == 1)
415  {
416  var temp = (Math.Pow(Math.Tan(nHatPreSpikeState / 2), 2) + Alpha * Io);
417  outputSpikeTimeDerivatives[neuronFiringHistory.SpikeStats.First().Synapse] =
419  {
420  OutputSpikeTimeToInputSpikeTimeDerivative = temp / (temp + Math.Pow(Alpha * nHatWeight, 2) + 2 * Alpha * nHatWeight + Math.Tan(nHatPreSpikeState / 2)),
421  OutputSpikeTimeToWeightDerivative = -Alpha / (Math.Pow(Math.Tan(nHatPostSpikeState / 2), 2) + Alpha * Io)
422  };
423  return outputSpikeTimeDerivatives;
424  }
425 
426  // Multi-Synapse Version
427  var nHatPostSpikeStateDerivative = StateDerivative(nHatPostSpikeState);
428  var postSpikeDerivativeProducts = CalculatePostSpikeDerivatives(neuronFiringHistory);
429  var n = 0;
430  foreach (var spikeStat in neuronFiringHistory.SpikeStats)
431  {
432  var prod = 1.0;
433  for (var j = n; j < nHat; j++)
434  {
435  prod *= postSpikeDerivativeProducts[j];
436  }
437  var gamma = -spikeStat.Weight * (Alpha * spikeStat.Weight + 2 * Math.Tan(spikeStat.PreSpikeState.StateVariable / 2));
438  var weightDerivative = ((-Alpha * (1 + Math.Cos(spikeStat.PostSpikeState.StateVariable))) / nHatPostSpikeStateDerivative) * prod;
439  outputSpikeTimeDerivatives[spikeStat.Synapse]=
441  {
442  OutputSpikeTimeToWeightDerivative = weightDerivative,
443  OutputSpikeTimeToInputSpikeTimeDerivative = gamma * weightDerivative
444  };
445  n++;
446  }
447 
448  return outputSpikeTimeDerivatives;
449  }
450 
456  public override IEnumerable<Spike> UpdateStateVariableOnSpike(double weight)
457  {
458  // The following will approach pi, but never exceed it as Phase->pi and weight->Inf
459  // So in effect, output spikes can never directly result from an input spike
460  State.StateVariable = 2 * Math.Atan(Alpha * weight + Math.Tan(State.StateVariable / 2));
461 
462  // Shortcut method that is slightly more effecient, but combines with AdvanceNeuronState as well
463  //State.Phase = 2 * Math.Atan(Alpha * spikeStats.Weight + c2);
464 
465  return new List<Spike>();
466  }
467 
473  public override IEnumerable<Spike> AdvanceNeuronState(double newTime)
474  {
475  var outputSpikeTimes = new List<Spike>();
476  var deltaTime = newTime - State.Time;
477  var initialState = State;
478  var c2 = GetC2(deltaTime);
479 
480  // Check for firing on trajectory; If Io>=0, we have to check the elapsed time between input spikes
481  // and compare that to the unforced output spike frequency.
482  if (Io > 0)
483  {
484  var thm = 2 * Math.Atan(c2);
485  if (thm < initialState.StateVariable || deltaTime > Period)
486  {
487  outputSpikeTimes.Add(new Spike {NeuronIndex = NeuronIndex , Time = NextSpikeTime});
488  // Check for case where phase completes multiple
489  // revolutions around phase circle between input spikes
490  if (deltaTime > Period)
491  {
492  var revCount = Math.Floor(deltaTime/Period);
493  for (var k = 2; k < revCount; k++)
494  {
495  outputSpikeTimes.Add(new Spike { NeuronIndex = NeuronIndex, Time = outputSpikeTimes.Last().Time + Period });
496  }
497  if (outputSpikeTimes.Last().Time + Period < newTime)
498  {
499  outputSpikeTimes.Add(new Spike { NeuronIndex = NeuronIndex, Time = outputSpikeTimes.Last().Time + Period });
500  }
501  }
502  }
503  State.StateVariable = thm;
504  }
505  // Check for firing on trajectory; If Io<0 the previous thp must be
506  // between the positive fixed point and pi and thm must be between
507  // -pi and the negative fixed point in order for the neuron to have
508  // fired on trajectory, regardless of the weight.
509  else
510  {
511  if (RemainingTimeToSpike < deltaTime)
512  {
513  outputSpikeTimes.Add(new Spike { NeuronIndex = NeuronIndex, Time = NextSpikeTime });
514  }
515  State.StateVariable = 2*Math.Atan(c2);
516 
517  if (outputSpikeTimes.Count > 0)
518  {
519  State.StateVariable = PostSpikeState;
520  }
521  }
522 
523  State.Time = newTime;
524  return outputSpikeTimes;
525  }
526 
532  private double GetC2(double deltaTime)
533  {
534  if (Io < 0)
535  {
536  return Beta * (C1 + Math.Tanh(-Beta * deltaTime)) / (1 + C1 * Math.Tanh(-Beta * deltaTime));
537  }
538  return Beta * (C1 + Math.Tan(Beta * deltaTime)) / (1 - C1 * Math.Tan(Beta * deltaTime));
539  }
540  }
541 }
OperatingRegion
Operating Region Enum
override string ToString()
The Neuron Properties As A String
Definition: ThetaNeuron.cs:252
override List< double > CalculatePostSpikeDerivatives(NeuronFiringHistory neuronFiringHistory)
Calculates Post Spike Derivatives, that is the derivate of the state after the current input spike re...
Definition: ThetaNeuron.cs:352
Spiking Neuron Abstract Class, Inherits From ISpikingNeuron
ThetaNeuron()
Creates a new instance of ThetaNeuron with no inputs
Definition: ThetaNeuron.cs:179
override Dictionary< Synapse, NeuronDerivativeParameters > CalculateOutputSpikeTimeDerivatives(NeuronFiringHistory neuronFiringHistory)
Calculates Output Spike Derivatives, that is the derivate of the output spike time relative to both t...
Definition: ThetaNeuron.cs:379
override IEnumerable< Spike > AdvanceNeuronState(double newTime)
Advances the neuron's state to newTime
Definition: ThetaNeuron.cs:473
ThetaNeuron(int numInputs=3, double weightIni=1e6, double newIo=DefaultIo)
Creates a new instance of ThetaNeuron with a given number of inputs and an initial weight and baselin...
Definition: ThetaNeuron.cs:215
List< Spike > OutputSpikes
Gets or sets a list of output spikes produced during this neuron firing history
ThetaNeuron(SpikingNeuronNetwork network, int neuronIndex, double newIo=DefaultIo)
Creates a new instance of ThetaNeuron an associates it to a network
Definition: ThetaNeuron.cs:190
override List< Spike > RunThetaNeuron(List< Spike > inputSpikes, int maxNumOutputSpikes=MaxNumOutputSpikes)
Runs the theta neuron, advancing the state and producing up to maxNumOutputSpikes output spikes in re...
Definition: ThetaNeuron.cs:277
The spike statistics class
Definition: SpikeStats.cs:9
override ISpikingNeuron Clone()
Clones a neuron by removing network association
Definition: ThetaNeuron.cs:236
override double StateDerivative(double phase)
Computes the derivative of the state relative to time
Definition: ThetaNeuron.cs:227
ThetaNeuron(IReadOnlyList< double > weights, double newIo=DefaultIo)
Creates a new instance of ThetaNeuron with given weights
Definition: ThetaNeuron.cs:202
override IEnumerable< Spike > UpdateStateVariableOnSpike(double weight)
Updates the neuron's state variable (phase), given a spike is currently being received on a synapse w...
Definition: ThetaNeuron.cs:456