1 namespace SpikingNeuronNetwork.Lib.NeuronModels
5 using System.Collections.Generic;
18 public static double Timestep = 0.0001;
23 public static double Epsilon = 1e-10;
28 public const int MaxNumOutputSpikes = 5;
33 protected Dictionary<Synapse, double>
_weights;
43 private readonly Random _random =
new Random();
48 public int NeuronIndex {
get; set; }
58 public bool Verbose {
get; set; }
73 public abstract double PostSpikeState {
get; }
78 public abstract double SpikeThreshold {
get; }
83 public abstract double InitialState {
get; }
88 public abstract double RemainingTimeToSpike {
get; }
93 public abstract double MaxNumericalIntegrationIterations {
get; }
103 public double NextSpikeTime
105 get {
return RemainingTimeToSpike + State.Time; }
111 public bool IsStandAloneNeuron
113 get {
return NeuronNetwork == null; }
123 return IsStandAloneNeuron
124 ? Weights.Count(x => Math.Abs(x.Value) > Epsilon)
125 : Weights.Where(x => x.Key.InputNeuronIndex < NeuronNetwork.NumInputs)
126 .Count(x => Math.Abs(x.Value) > Epsilon);
133 public Dictionary<Synapse, double> Weights
135 get {
return IsStandAloneNeuron ? _weights : NeuronNetwork.GetNeuronWeights(NeuronIndex); }
153 NeuronNetwork = network;
154 NeuronIndex = neuronIndex;
157 Method = SimulationMethod.EventDriven;
166 var numInputs = weights.Count;
167 NeuronIndex = numInputs;
168 _weights =
new Dictionary<Synapse, double>(weights.Count);
169 for (var k = 0; k < numInputs; k++)
171 _weights.Add(
new Synapse(k, NeuronIndex), weights[k]);
175 Method = SimulationMethod.Numerical;
185 _weights =
new Dictionary<Synapse, double>(numInputs);
186 NeuronIndex = numInputs;
187 for (var k = 0; k < numInputs; k++)
189 var newWeight = weightIni > 100 ? 2*_random.NextDouble() - 1 : weightIni;
190 _weights.Add(
new Synapse(k, NeuronIndex), newWeight);
194 Method = SimulationMethod.Numerical;
210 public abstract List<Spike> RunThetaNeuron(List<Spike> inputSpikes,
int maxNumOutputSpikes = MaxNumOutputSpikes);
217 public abstract IEnumerable<Spike> UpdateStateVariableOnSpike(
double weight);
224 public abstract IEnumerable<Spike> AdvanceNeuronState(
double newTime);
232 public abstract List<double> CalculatePostSpikeDerivatives(
NeuronFiringHistory neuronFiringHistory);
240 public abstract Dictionary<Synapse, NeuronDerivativeParameters> CalculateOutputSpikeTimeDerivatives(
NeuronFiringHistory neuronFiringHistory);
247 public abstract double StateDerivative(
double stateVariable);
257 var outputSpikes =
new List<Spike>();
258 if (SpikeQueue.Count == 0)
262 var statePast = State.StateVariable;
263 var allInputSpikeProcessed =
false;
264 var currentSpike = SpikeQueue.Dequeue();
267 var intLimit = MaxNumericalIntegrationIterations;
269 var doIntegration =
true;
270 while (doIntegration)
273 State.StateVariable = statePast + Timestep * StateDerivative(statePast);
275 if (!allInputSpikeProcessed && (currentSpike.Time >= State.Time) && (currentSpike.Time < (State.Time + Timestep)))
277 var synapse =
new Synapse(currentSpike.NeuronIndex, NeuronIndex);
283 PreSpikeState =
new NeuronState {StateVariable = State.StateVariable, Time = currentSpike.Time},
284 Weight = Weights[synapse],
288 catch (KeyNotFoundException)
290 Console.WriteLine(
"Synapse: " + synapse);
291 Console.WriteLine(
"Weights: " + GetWeightsString(Weights));
294 UpdateStateVariableOnSpike(spikeStats.Weight);
295 spikeStats.PostSpikeState =
new NeuronState { StateVariable = State.StateVariable, Time = currentSpike.Time };
298 Console.WriteLine(
"*** Spike From Neuron " + currentSpike.NeuronIndex +
" To Neuron " + NeuronIndex +
" ***");
299 Console.WriteLine(spikeStats);
301 if (SpikeQueue.Count > 0)
303 currentSpike = SpikeQueue.Dequeue();
307 allInputSpikeProcessed =
true;
319 if (State.StateVariable > SpikeThreshold && statePast <= SpikeThreshold)
321 outputSpikes.Add(
new Spike { NeuronIndex = NeuronIndex, Time = State.Time });
324 Console.WriteLine(
"---> " + outputSpikes.Last() +
"\n");
326 State.StateVariable = PostSpikeState;
327 if (outputSpikes.Count == maxNumOutputSpikes)
334 if (State.StateVariable > PostSpikeState && statePast <= PostSpikeState)
336 State.StateVariable = SpikeThreshold - (State.StateVariable + PostSpikeState);
340 statePast = State.StateVariable;
341 State.Time += Timestep;
342 if (State.Time <= intLimit)
continue;
346 doIntegration =
false;
362 var postSpikeDerivativeProducts =
new List<double>();
364 var initialState = State;
365 const double offset = 0.001;
366 foreach (var spikeStat
in neuronFiringHistory.SpikeStats)
368 if (previousSpikeStat == null)
370 previousSpikeStat = spikeStat;
374 var currentSpike =
new Spike
376 NeuronIndex = spikeStat.Synapse.InputNeuronIndex,
377 Time = spikeStat.PreSpikeState.Time
380 State =
new NeuronState { StateVariable = previousSpikeStat.PostSpikeState.StateVariable, Time = previousSpikeStat.PostSpikeState.Time };
381 State.StateVariable -= offset;
383 ProcessSpike(currentSpike, out spikeStatsGradDelta1);
384 State =
new NeuronState { StateVariable = previousSpikeStat.PostSpikeState.StateVariable, Time = previousSpikeStat.PostSpikeState.Time };
385 State.StateVariable += offset;
387 ProcessSpike(currentSpike, out spikeStatsGradDelta2);
389 postSpikeDerivativeProducts.Add((spikeStatsGradDelta2.PostSpikeState.StateVariable - spikeStatsGradDelta1.PostSpikeState.StateVariable) / (2 * offset));
390 previousSpikeStat = spikeStat;
393 State = initialState;
394 return postSpikeDerivativeProducts;
406 var inputSpikes = neuronFiringHistory.SpikeStats.Select(spikeStat =>
new Spike
408 NeuronIndex = spikeStat.Synapse.InputNeuronIndex,
409 Time = spikeStat.PreSpikeState.Time
414 throw new ArgumentException(
"MIMO Training Not Implemented");
419 throw new ArgumentException(
"Actual Output Spike Times are Missing");
422 var inputSynapses = Weights.Select(x => x.Key).ToList();
425 OutputSpikeTimeToInputSpikeTimeDerivative = 0, OutputSpikeTimeToWeightDerivative = 0
428 const double offset = 0.00001;
429 var initialState = State;
430 foreach (var spikeStat
in neuronFiringHistory.SpikeStats)
433 var weight = Weights.First(x => x.Key.InputNeuronIndex == spikeStat.Synapse.InputNeuronIndex);
435 SetSynapticWeight(spikeStat.Synapse, weight.Value - offset);
437 var outputSpikeTime1 = RunThetaNeuron(inputSpikes).First().Time;
439 SetSynapticWeight(spikeStat.Synapse, weight.Value + offset);
441 var outputSpikeTime2 = RunThetaNeuron(inputSpikes).First().Time;
443 SetSynapticWeight(spikeStat.Synapse, weight.Value);
444 var outputToWeightDerivative = (outputSpikeTime2 - outputSpikeTime1) / (2 * offset);
447 var currentSpike = inputSpikes.First(x => x.NeuronIndex == spikeStat.Synapse.InputNeuronIndex);
449 currentSpike.Time -= offset;
451 var outputSpikeTime3 = RunThetaNeuron(inputSpikes).First().Time;
453 currentSpike.Time += 2*offset;
455 var outputSpikeTime4 = RunThetaNeuron(inputSpikes).First().Time;
457 var outputToInputSpikeTimeDerivative = (outputSpikeTime4 - outputSpikeTime3) / (2 * offset);
461 OutputSpikeTimeToWeightDerivative = outputToWeightDerivative,
462 OutputSpikeTimeToInputSpikeTimeDerivative = outputToInputSpikeTimeDerivative
464 outputSpikeTimeDerivatives[spikeStat.Synapse] = derivativeParameters;
467 State = initialState;
468 return outputSpikeTimeDerivatives;
484 if (State.Time > spike.
Time)
487 return new List<Spike>();
490 var outputSpikeTimes = AdvanceNeuronState(spike.
Time).ToList();
495 StateVariable = State.StateVariable,
498 Weight = Weights.First(x => x.Key.InputNeuronIndex == spike.NeuronIndex).Value,
502 var updateSpikes = UpdateStateVariableOnSpike(spikeStats.Weight).ToList();
503 var firedOnSpike = updateSpikes.Count > 0;
504 outputSpikeTimes.AddRange(updateSpikes);
508 StateVariable = State.StateVariable,
514 Console.WriteLine(
"*** Spike From Neuron " + spike.NeuronIndex +
" To Neuron " + NeuronIndex +
" ***");
515 Console.WriteLine(spikeStats);
516 if (outputSpikeTimes.Count > 0)
518 foreach (var outputSpikeTime
in outputSpikeTimes)
520 Console.WriteLine(
"---> " + outputSpikeTime);
525 Console.WriteLine(
"---> No Output Spikes Produced");
530 Console.WriteLine(
"---> At least one output spike fired directly from input spike");
536 return outputSpikeTimes;
546 StateVariable = InitialState,
558 if (IsStandAloneNeuron)
560 if (_weights.ContainsKey(synapse))
562 _weights[synapse] = newValue;
566 _weights.Add(synapse, newValue);
571 NeuronNetwork.SetSynapticWeight(synapse, newValue);
582 var weightsStringBuilder =
new StringBuilder();
583 weights.Keys.ToList().ForEach(x => weightsStringBuilder.AppendLine(
"\t" + weights[x] +
"(" + x.InputNeuronIndex +
"->" + x.OutputNeuronIndex +
")"));
584 return weightsStringBuilder.ToString();
OperatingRegion
Operating Region Enum
SpikingNeuron()
Creates a new instance of SpikingNeuron with no inputs
List< Spike > RunThetaNeuronNumerically(int maxNumOutputSpikes=MaxNumOutputSpikes)
Runs the theta neuron numerically, advancing the state and producing up to maxNumOutputSpikes output ...
List< double > CalculatePostSpikeDerivativesNumerical(NeuronFiringHistory neuronFiringHistory)
Calculates Post Spike Derivatives numerically, that is the derivate of the state after the current in...
double Time
Gets or sets the time.
void SetSynapticWeight(Synapse synapse, double newValue)
Sets synaptic weight to newValue
SpikingNeuron(IReadOnlyList< double > weights)
Creates a new instance of SpikingNeuron with given weights
Spiking Neuron Abstract Class, Inherits From ISpikingNeuron
SpikingNeuron(SpikingNeuronNetwork network, int neuronIndex)
Creates a new instance of SpikingNeuron an associates it to a network
Spike Priority Queue used for determining what spike to process next based on spike timing ...
Neuron Derivative Parameters Class
List< Spike > OutputSpikes
Gets or sets a list of output spikes produced during this neuron firing history
SimulationMethod
Simulation Method Enum
void ResetState()
Resets the neuron state to its initial conditions
The spike statistics class
SpikePriorityQueue SpikeQueue
Spike Queue
SpikingNeuron(int numInputs=3, double weightIni=1e6)
Creates a new instance of SpikingNeuron with a given number of inputs and an initial weight ...
Spiking Neuron Network Class
int NeuronIndex
Gets or sets the index of the neuron.
static string GetWeightsString(Dictionary< Synapse, double > weights)
Get a string representation of the weights dictionary
IEnumerable< Spike > ProcessSpike(Spike spike, out SpikeStats spikeStats)
Starting from any state, process the neuron state forward in time until the input spike time and proc...
Neuron Firing History Class
Dictionary< Synapse, NeuronDerivativeParameters > CalculateOutputSpikeTimeDerivativesNumerical(NeuronFiringHistory neuronFiringHistory)
Calculates Output Spike Derivatives numerically, that is the derivate of the output spike time relati...
Dictionary< Synapse, double > _weights
Weights backing field