using System.Collections.Generic;
public interface ISynapse
double Weight { get; set; }
double PreviousWeight { get; set; }
bool IsFromNeuron(Guid fromNeuronId);
void UpdateWeight(double learningRate, double delta);
public class Synapse : ISynapse
internal INeuron _fromNeuron;
internal INeuron _toNeuron;
public double Weight { get; set; }
public double PreviousWeight { get; set; }
public Synapse(INeuron fromNeuraon, INeuron toNeuron, double weight)
_fromNeuron = fromNeuraon;
public static Random rand = new Random();
public Synapse(INeuron fromNeuraon, INeuron toNeuron)
_fromNeuron = fromNeuraon;
Weight = rand.NextDouble();
public double GetOutput()
return _fromNeuron.CalculateOutput();
public bool IsFromNeuron(Guid fromNeuronId)
return _fromNeuron.Id.Equals(fromNeuronId);
public void UpdateWeight(double learningRate, double delta)
Weight += learningRate * delta;
public class InputSynapse : ISynapse
internal INeuron _toNeuron;
public double Weight { get; set; }
public double Output { get; set; }
public double PreviousWeight { get; set; }
public InputSynapse(INeuron toNeuron)
public InputSynapse(INeuron toNeuron, double output)
public double GetOutput()
public bool IsFromNeuron(Guid fromNeuronId)
public void UpdateWeight(double learningRate, double delta)
throw new InvalidOperationException("It is not allowed to call this method on Input Connecion");
public interface IInputFunction
double CalculateInput(List<ISynapse> inputs);
public class WeightedSumFunction : IInputFunction
public double CalculateInput(List<ISynapse> inputs)
return inputs.Select(x => x.Weight * x.GetOutput()).Sum();
public interface IActivationFunction
double CalculateOutput(double input);
public class StepActivationFunction : IActivationFunction
private double _treshold;
public StepActivationFunction(double treshold)
public double CalculateOutput(double input)
return Convert.ToDouble(input > _treshold);
public class SigmoidActivationFunction : IActivationFunction
private double _coeficient;
public SigmoidActivationFunction(double coeficient)
_coeficient = coeficient;
public double CalculateOutput(double input)
return (1 / (1 + Math.Exp(-input * _coeficient)));
public class RectifiedActivationFuncion : IActivationFunction
public double CalculateOutput(double input)
return Math.Max(0, input);
double PreviousPartialDerivate { get; set; }
List<ISynapse> Inputs { get; set; }
List<ISynapse> Outputs { get; set; }
void AddInputNeuron(INeuron inputNeuron);
void AddOutputNeuron(INeuron inputNeuron);
double CalculateOutput();
void AddInputSynapse(double inputValue);
void PushValueOnInput(double inputValue);
public class Neuron : INeuron
private IActivationFunction _activationFunction;
private IInputFunction _inputFunction;
public List<ISynapse> Inputs { get; set; }
public List<ISynapse> Outputs { get; set; }
public Guid Id { get; private set; }
public double PreviousPartialDerivate { get; set; }
public Neuron(IActivationFunction activationFunction, IInputFunction inputFunction)
Inputs = new List<ISynapse>();
Outputs = new List<ISynapse>();
_activationFunction = activationFunction;
_inputFunction = inputFunction;
public void AddInputNeuron(INeuron inputNeuron)
var synapse = new Synapse(inputNeuron, this);
inputNeuron.Outputs.Add(synapse);
public void AddOutputNeuron(INeuron outputNeuron)
var synapse = new Synapse(this, outputNeuron);
outputNeuron.Inputs.Add(synapse);
public double CalculateOutput()
return _activationFunction.CalculateOutput(_inputFunction.CalculateInput(this.Inputs));
public void AddInputSynapse(double inputValue)
var inputSynapse = new InputSynapse(this, inputValue);
Inputs.Add(inputSynapse);
public void PushValueOnInput(double inputValue)
((InputSynapse)Inputs.First()).Output = inputValue;
public List<INeuron> Neurons;
Neurons = new List<INeuron>();
public void ConnectLayers(NeuralLayer inputLayer)
var combos = Neurons.SelectMany(neuron => inputLayer.Neurons, (neuron, input) => new { neuron, input });
combos.ToList().ForEach(x => x.neuron.AddInputNeuron(x.input));
public class NeuralLayerFactory
public NeuralLayer CreateNeuralLayer(int numberOfNeurons, IActivationFunction activationFunction, IInputFunction inputFunction)
var layer = new NeuralLayer();
for (int i = 0; i < numberOfNeurons; i++)
var neuron = new Neuron(activationFunction, inputFunction);
layer.Neurons.Add(neuron);
public class SimpleNeuralNetwork
private NeuralLayerFactory _layerFactory;
internal List<NeuralLayer> _layers;
internal double _learningRate;
internal double[][] _expectedResult;
public SimpleNeuralNetwork(int numberOfInputNeurons)
_layers = new List<NeuralLayer>();
_layerFactory = new NeuralLayerFactory();
CreateInputLayer(numberOfInputNeurons);
public void AddLayer(NeuralLayer newLayer)
var lastLayer = _layers.Last();
newLayer.ConnectLayers(lastLayer);
public void PushInputValues(double[] inputs)
_layers.First().Neurons.ForEach(x => x.PushValueOnInput(inputs[_layers.First().Neurons.IndexOf(x)]));
public void PushExpectedValues(double[][] expectedOutputs)
_expectedResult = expectedOutputs;
public List<double> GetOutput()
var returnValue = new List<double>();
_layers.Last().Neurons.ForEach(neuron =>
returnValue.Add(neuron.CalculateOutput());
public void Train(double[][] inputs, int numberOfEpochs)
for(int i = 0; i < numberOfEpochs; i++)
for(int j = 0; j < inputs.GetLength(0); j ++)
PushInputValues(inputs[j]);
var outputs = new List<double>();
_layers.Last().Neurons.ForEach(x =>
outputs.Add(x.CalculateOutput());
totalError = CalculateTotalError(outputs, j);
private void CreateInputLayer(int numberOfInputNeurons)
var inputLayer = _layerFactory.CreateNeuralLayer(numberOfInputNeurons, new RectifiedActivationFuncion(), new WeightedSumFunction());
inputLayer.Neurons.ForEach(x => x.AddInputSynapse(0));
this.AddLayer(inputLayer);
private double CalculateTotalError(List<double> outputs, int row)
outputs.ForEach(output =>
var error = Math.Pow(output - _expectedResult[row][outputs.IndexOf(output)], 2);
private void HandleOutputLayer(int row)
_layers.Last().Neurons.ForEach(neuron =>
neuron.Inputs.ForEach(connection =>
var output = neuron.CalculateOutput();
var netInput = connection.GetOutput();
var expectedOutput = _expectedResult[row][_layers.Last().Neurons.IndexOf(neuron)];
var nodeDelta = (expectedOutput - output) * output * (1 - output);
var delta = netInput * nodeDelta;
connection.UpdateWeight(_learningRate, delta);
neuron.PreviousPartialDerivate = nodeDelta;
private void HandleHiddenLayers()
for (int k = _layers.Count - 2; k > 0; k--)
_layers[k].Neurons.ForEach(neuron =>
neuron.Inputs.ForEach(connection =>
var output = neuron.CalculateOutput();
var netInput = connection.GetOutput();
outputNeuron.Inputs.Where(i => i.IsFromNeuron(neuron.Id))
.ForEach(outConnection =>
sumPartial += outConnection.PreviousWeight * outputNeuron.PreviousPartialDerivate;
var delta = netInput * sumPartial * output * (1 - output);
connection.UpdateWeight(_learningRate, delta);
public double[] DivideDoubleArray(double[] arr, double divideBy)
for (int i = 0; i < arr.Length; i++)
public static void Main()
var network = new SimpleNeuralNetwork(7);
var layerFactory = new NeuralLayerFactory();
network.AddLayer(layerFactory.CreateNeuralLayer(4, new SigmoidActivationFunction(1), new WeightedSumFunction()));
network.AddLayer(layerFactory.CreateNeuralLayer(1, new SigmoidActivationFunction(1), new WeightedSumFunction()));
network.PushExpectedValues(
network.DivideDoubleArray(new double[] { 1 }, 10),
network.DivideDoubleArray(new double[] { 2 }, 10),
network.DivideDoubleArray(new double[] { 3 }, 10),
network.DivideDoubleArray(new double[] { 4 }, 10),
network.DivideDoubleArray(new double[] { 5 }, 10),
network.DivideDoubleArray(new double[] { 6 }, 10),
network.DivideDoubleArray(new double[] { 7 }, 10),
network.DivideDoubleArray(new double[] { 8 }, 10),
network.DivideDoubleArray(new double[] { 9 }, 10),
network.DivideDoubleArray(new double[] { 0 }, 10),
Console.WriteLine("Training...");
network.DivideDoubleArray(new double[] { 4, 12, 4, 4, 4, 4, 4 }, 31),
network.DivideDoubleArray(new double[] { 6, 9, 1, 2, 4, 8, 15 }, 31),
network.DivideDoubleArray(new double[] { 6, 9, 1, 6, 1, 9, 6 }, 31),
network.DivideDoubleArray(new double[] { 3, 5, 5, 9, 15, 1, 1 }, 31),
network.DivideDoubleArray(new double[] { 14, 8, 8, 14, 1, 1, 14 }, 31),
network.DivideDoubleArray(new double[] { 6, 9, 16, 14, 17, 17, 14 }, 31),
network.DivideDoubleArray(new double[] { 14, 1, 1, 2, 2, 4, 4 }, 31),
network.DivideDoubleArray(new double[] { 6, 9, 9, 6, 9, 9, 6 }, 31),
network.DivideDoubleArray(new double[] { 6, 9, 9, 9, 7, 1, 6 }, 31),
network.DivideDoubleArray(new double[] { 6, 9, 9, 9, 9, 9, 6 }, 31),
Console.WriteLine("Testing...");
network.PushInputValues(network.DivideDoubleArray(new double[] { 4, 12, 4, 4, 4, 4, 4 }, 31));
var outputs = network.GetOutput();
Console.WriteLine($"Input: 1; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 6, 9, 1, 2, 4, 8, 15 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 2; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 6, 9, 1, 6, 1, 9, 6 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 3; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 3, 5, 5, 9, 15, 1, 1 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 4; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 14, 8, 8, 14, 1, 1, 14 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 5; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 6, 9, 16, 14, 17, 17, 14 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 6; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 14, 1, 1, 2, 2, 4, 4 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 7; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 6, 9, 9, 6, 9, 9, 6 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 8; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 6, 9, 9, 9, 7, 1, 6 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 9; Output: {string.Join(',', outputs)}");
network.PushInputValues(network.DivideDoubleArray(new double[] { 6, 9, 9, 9, 9, 9, 6 }, 31));
outputs = network.GetOutput();
Console.WriteLine($"Input: 0; Output: {string.Join(',', outputs)}");