static void Main(string[] args) { // NEAT XOR Test NEATConfig config = new NEATConfig(); NEAT neat = new NEAT(2, 1, config); Individual champion = neat.runUntil(15.95, (indiv) => { double[][] inputs = { new double[] { 0, 0 }, new double[] { 0, 1 }, new double[] { 1, 0 }, new double[] { 1, 1 } }; double[] expectedOutput = new double[] { 0, 1, 1, 0 }; double fitness = 0; for (int i = 0; i < 4; i++) { fitness += Math.Abs(expectedOutput[i] - indiv.eval(inputs[i])[0]); } return(Math.Pow(4 - fitness, 2)); }); if (System.Diagnostics.Debugger.IsAttached) { Console.Write("Press a key to terminate:"); Console.ReadLine(); } }
public NEAT(int nbInputs, int nbOutputs, NEATConfig config) { this.config = config; Console.WriteLine("Generating initial population"); population = new Population(nbInputs, nbOutputs, config); Console.WriteLine("Done"); }
public Population(int nbInputs, int nbOutputs, NEATConfig config) { this.config = config; // Initialize a population with the input and output layers fully connected individuals = new Individual[config.populationSize]; for (int i = 0; i < individuals.Length; i++) { individuals[i] = new Individual(NeuralGraph.generateFullyConnected(nbInputs + 1, nbOutputs)); // Add a bias input } }
public double distanceFrom(Individual indiv, NEATConfig config) { List <Connection> connections = graph.getConnectionList(); List <Connection> otherConnections = indiv.getGraph().getConnectionList(); int N = Math.Max(connections.Count, otherConnections.Count); int D = 0, E = 0; // Number of Disjoint (D) and Excess (E) connections double W = 0; // Total weight difference int weightCntr = 0; List <Connection> .Enumerator it1 = connections.GetEnumerator(); List <Connection> .Enumerator it2 = otherConnections.GetEnumerator(); bool hasNext1 = it1.MoveNext(), hasNext2 = it2.MoveNext(); while (hasNext1 || hasNext2) { if (!hasNext1) { E++; hasNext2 = it2.MoveNext(); continue; } if (!hasNext2) { E++; hasNext1 = it1.MoveNext(); continue; } int innov1 = it1.Current.getInnovation(); int innov2 = it2.Current.getInnovation(); if (innov1 == innov2) { weightCntr++; W += Math.Abs(it1.Current.getWeight() - it2.Current.getWeight()); hasNext1 = it1.MoveNext(); hasNext2 = it2.MoveNext(); } else if (innov1 > innov2) { D++; hasNext2 = it2.MoveNext(); } else { D++; hasNext1 = it1.MoveNext(); } } return((config.c1 * E) / N + (config.c2 * D) / N + ((weightCntr > 0) ? ((config.c3 * W) / weightCntr) : 0)); }
public Individual mutate(NEATConfig config) { Individual newIndiv = clone(); NeuralGraph newGraph = newIndiv.getGraph(); if (Utils.rand.NextDouble() < config.newConnectionProb) { // Select two random neurons and create (or enable) a link in-between int hiddenCount = newGraph.getHiddenNeurons().Count(); int inputCount = newGraph.getInputNeurons().Count(); int outputCount = newGraph.getOutputNeurons().Count(); // Select the input and output neurons int sourceIndex = Utils.rand.Next(hiddenCount + inputCount + outputCount); int destIndex = Utils.rand.Next(hiddenCount + outputCount); Neuron source, dest; if (sourceIndex < hiddenCount) { source = newGraph.getHiddenNeurons()[sourceIndex]; } else if (sourceIndex < hiddenCount + inputCount) { source = newGraph.getInputNeurons()[sourceIndex - hiddenCount]; } else { source = newGraph.getOutputNeurons()[sourceIndex - hiddenCount - inputCount]; } if (destIndex < hiddenCount) { dest = newGraph.getHiddenNeurons()[destIndex]; } else { dest = newGraph.getOutputNeurons()[destIndex - hiddenCount]; } Connection connection; if (!newGraph.getConnections()[dest].TryGetValue(source, out connection)) { connection = new Connection(source, dest, -1); // TODO what the crap is the parent innovation supposed to be here newGraph.addConnection(connection); } else { connection.enable(); } } if (Utils.rand.NextDouble() < config.newNodeProb) { // When adding a node, disable a connection and replace it by a neuron and two connections // The new input connection to that neuron has weight of 1, while output has same weight as disabled connection Connection connection = newGraph.getConnectionList()[Utils.rand.Next(newGraph.getConnectionList().Count)]; connection.disable(); Neuron neuron = new Neuron(NeuronType.Hidden); Connection newConn1 = new Connection(connection.getSource(), neuron, connection.getInnovation()); Connection newConn2 = new Connection(neuron, connection.getDest(), connection.getInnovation()); newGraph.addHiddenNeuron(neuron); newGraph.addConnection(newConn1); newGraph.addConnection(newConn2); } // Mutate each connection weight foreach (Connection connection in newGraph.getConnectionList()) { if (Utils.rand.NextDouble() < config.mutationProb) { if (Utils.rand.NextDouble() < config.uniformPerturbationMutationProb) { connection.setWeight(connection.getWeight() + Utils.rand.NextDouble() * 2 * config.perturbationStep - config.perturbationStep); } else { connection.setWeight(Utils.nextGaussian(0, 1)); } } } return(newIndiv); }
public static Individual cross(Individual parent1, Individual parent2, NEATConfig config) { NeuralGraph graph1 = parent1.getGraph(); NeuralGraph graph2 = parent2.getGraph(); Dictionary <Neuron, Neuron> addedNeurons = new Dictionary <Neuron, Neuron>(); NeuralGraph child = new NeuralGraph(graph1.getInputNeurons(), graph1.getOutputNeurons()); // Add connections from both parents List <Connection> .Enumerator it1 = graph1.getConnectionList().GetEnumerator(); List <Connection> .Enumerator it2 = graph2.getConnectionList().GetEnumerator(); bool hasNext1 = it1.MoveNext(), hasNext2 = it2.MoveNext(); while (hasNext1 || hasNext2) { if (!hasNext1) { addClonedConnection(it2.Current, child, addedNeurons); hasNext2 = it2.MoveNext(); continue; } if (!hasNext2) { addClonedConnection(it1.Current, child, addedNeurons); hasNext1 = it1.MoveNext(); continue; } int innov1 = it1.Current.getInnovation(); int innov2 = it2.Current.getInnovation(); if (innov1 == innov2) { // If either parent's connection is disabled, there is a chance for it to be disabled in the child bool disabled = false; if (it1.Current.isDisabled() != it2.Current.isDisabled()) { disabled = (Utils.rand.NextDouble() < config.disableGeneProb); } else if (it1.Current.isDisabled()) { disabled = true; } Connection connectionToAdd = (Utils.rand.NextDouble() < 0.5) ? it1.Current : it2.Current; addClonedConnection(connectionToAdd, child, addedNeurons, disabled); hasNext1 = it1.MoveNext(); hasNext2 = it2.MoveNext(); } else if (innov1 > innov2) { addClonedConnection(it2.Current, child, addedNeurons); hasNext2 = it2.MoveNext(); } else { addClonedConnection(it1.Current, child, addedNeurons); hasNext1 = it1.MoveNext(); } } return(new Individual(child)); }