Example #1
0
        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();
            }
        }
Example #2
0
        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");
        }
Example #3
0
        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
            }
        }
Example #4
0
        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));
        }
Example #5
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);
        }
Example #6
0
        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));
        }