Пример #1
0
        public void InnovationNumberTest()
        {
            // Genome
            Genome gen1 = new Genome(r);

            Assert.IsTrue(gen1.GetInnovationNumber() == 0);

            // Create 3 sensors
            gen1.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            gen1.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            gen1.AddNode(new Node(Node.ENodeType.SENSOR, 3));

            // Create 1 output
            gen1.AddNode(new Node(Node.ENodeType.OUTPUT, 4));

            // Create 1 hidden node
            gen1.AddNode(new Node(Node.ENodeType.HIDDEN, 5));

            // Add connections from the paper
            gen1.AddConnectionGene(1, 4, 0.5f);
            Assert.IsTrue(gen1.GetInnovationNumber() == 1);

            gen1.AddConnectionGene(2, 4, false);
            Assert.IsTrue(gen1.GetInnovationNumber() == 2);
            gen1.AddConnectionGene(3, 4);
            Assert.IsTrue(gen1.GetInnovationNumber() == 3);
            gen1.AddConnectionGene(2, 5);
            Assert.IsTrue(gen1.GetInnovationNumber() == 4);
            gen1.AddConnectionGene(5, 4);
            Assert.IsTrue(gen1.GetInnovationNumber() == 5);
            gen1.AddConnectionGene(1, 5, true);
            Assert.IsTrue(gen1.GetInnovationNumber() == 6);
        }
Пример #2
0
        public void SetupTest()
        {
            r = new Random(0);

            // Genome
            gen = new Genome(r);

            // Create 3 sensors
            gen.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            gen.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            gen.AddNode(new Node(Node.ENodeType.SENSOR, 3));

            // Create 1 output
            gen.AddNode(new Node(Node.ENodeType.OUTPUT, 4));

            // Create 1 hidden node
            gen.AddNode(new Node(Node.ENodeType.HIDDEN, 5));

            // Add connections from the paper
            gen.AddConnectionGene(1, 4, 0.5f);
            gen.AddConnectionGene(2, 4, false);
            gen.AddConnectionGene(3, 4);
            gen.AddConnectionGene(2, 5);
            gen.AddConnectionGene(5, 4);
            gen.AddConnectionGene(1, 5, 8, true);
        }
Пример #3
0
    public void Initialize(int input, int output)
    {
        Genome genome = new Genome();

        for (int i = 0; i < input; i++)
        {
            Node node = new Node(Node.NodeType.INPUT, i + 1);
            genome.AddNode(node);
        }
        for (int i = input; i < output + input; i++)
        {
            Node node = new Node(Node.NodeType.OUTPUT, i + 1);
            genome.AddNode(node);
        }

        int inno = 0;

        for (int i = 1; i <= input; i++)
        {
            for (int j = input + 1; j <= output + input; j++)
            {
                genome.AddConnection(new Connection(i, j, 0f, true, ++inno));
            }
        }

        _brain       = genome;
        _initialized = true;
    }
Пример #4
0
    public void Start()
    {
        vision.Add(0);
        vision.Add(1);
        vision.Add(2);
        for (int i = 0; i < 3; i++)
        {
            Node node = new Node(Node.NodeType.INPUT, i + 1);
            genome.AddNode(node);
        }
        genome.AddNode(new Node(Node.NodeType.OUTPUT, 4));
        genome.AddNode(new Node(Node.NodeType.OUTPUT, 5));
        genome.AddNode(new Node(Node.NodeType.OUTPUT, 6));
        //genome.AddNode(new Node(Node.NodeType.HIDDEN, 5));
        //genome.AddNode(new Node(Node.NodeType.HIDDEN, 6));


        genome.AddConnection(new Connection(1, 4, 0f, true, 1));
        genome.AddConnection(new Connection(2, 5, 0f, true, 2));
        genome.AddConnection(new Connection(3, 6, 0f, true, 3));

        foreach (Connection c in genome.Connections.Values)
        {
            History.AddConnectionToInnovationHistoryDebug(c);
        }

        gameObject.GetComponent <GenomePrinter>().Draw(genome);
        Genome.DebugPrint(genome);

        History.SetInnovationDebug(3);
    }
Пример #5
0
    private void Start()
    {
        for (int i = 0; i < 2; i++)
        {
            Node node = new Node(Node.NodeType.INPUT, i + 1);
            genome.AddNode(node);
        }

        genome.AddNode(new Node(Node.NodeType.OUTPUT, 3));
        genome.AddNode(new Node(Node.NodeType.HIDDEN, 4));


        genome.AddConnection(new Connection(1, 4, 0.2f, true, 1));
        genome.AddConnection(new Connection(2, 4, 0.4f, true, 2));
        genome.AddConnection(new Connection(4, 3, 1f, true, 3));
        genome.AddConnection(new Connection(1, 3, -0.3f, true, 4));

        History.SetInnovationDebug(4);

        /*
         * for (int i = 0; i < 30; i++)
         * {
         *  genome.Mutate();
         * }
         *
         */

        ForwardProp();
        GetComponent <GenomePrinter>().Draw(genome);
    }
Пример #6
0
        public void AddGenomesToSpeciesTest()
        {
            UnitTests.RandomStub randomStub = new UnitTests.RandomStub();
            Simulation           sim        = new Simulation(randomStub, gen, 10);

            // Create some fake species and genomes
            Species species1 = new Species(randomStub);

            // Two genomes are matching, one is not so we should get 2 species (with 2 and 1 Genomes respectively)
            Genome gen1 = new Genome(randomStub);
            Genome gen2 = new Genome(randomStub);

            Genome gen3 = new Genome(randomStub);

            gen3.ParentSimulation = sim;
            gen3.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            gen3.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            gen3.AddNode(new Node(Node.ENodeType.OUTPUT, 3));
            gen3.AddNode(new Node(Node.ENodeType.OUTPUT, 4));
            gen3.AddConnectionGene(1, 2, true);
            gen3.AddConnectionGene(1, 3, true);
            gen3.AddConnectionGene(1, 4, true);


            sim.Species.Clear();

            sim.AddGenomesToSpecies(new List <Genome> {
                gen1, gen2, gen3
            });

            Assert.AreEqual(2, sim.Species.Count);
            Assert.AreEqual(2, sim.Species[0].Genomes.Count);
            Assert.AreEqual(1, sim.Species[1].Genomes.Count);
        }
Пример #7
0
        public void InsertGeneTest_Null_ExpectedException()
        {
            UnitTests.RandomStub randomStub = new UnitTests.RandomStub();
            randomStub.NextIntValue = 0; randomStub.NextDoubleValue = 0.0;

            Genome genome = new Genome(randomStub);

            genome.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            genome.AddNode(new Node(Node.ENodeType.OUTPUT, 2));

            genome.InsertConnectionGene(null);
        }
Пример #8
0
        public void FindConnectionGeneToSplitTest_NoGenes_Expected_False()
        {
            Genome genome = new Genome(r);

            genome.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            genome.AddNode(new Node(Node.ENodeType.OUTPUT, 2));

            int  connectionIndex = -1;
            bool found           = genome.FindConnectionGeneToSplit(out connectionIndex);

            Assert.AreEqual(false, found);
            Assert.AreEqual(-1, connectionIndex);
        }
Пример #9
0
    public static Genome Crossover(Genome parent1, Genome parent2, Random r)      //Creates a child genome from two parent genomes. Parent 1 has higher fitness.
    {
        Genome child = new Genome();

        List <Genome.NodeGene> parent1nodes = parent1.GetNodes();
        Dictionary <int, Genome.ConnectionGene> parent1connections = parent1.GetConnections();
        Dictionary <int, Genome.ConnectionGene> parent2connections = parent2.GetConnections();

        foreach (Genome.NodeGene p1node in parent1nodes)
        {
            child.AddNode(p1node);
        }

        foreach (Genome.ConnectionGene p1con in parent1connections.Values)
        {
            if (parent2connections.ContainsKey(p1con.GetInnovation()))
            {
                child.AddConnection(r.Next(100) < 50 ? p1con : parent2connections[p1con.GetInnovation()]);
            }
            else
            {
                child.AddConnection(p1con);
            }
        }

        return(child);
    }
Пример #10
0
        public void FindConnectionGeneToSplitTest_Correct_Expected_True()
        {
            UnitTests.RandomStub randomStub = new UnitTests.RandomStub();
            randomStub.NextIntValue = 0; randomStub.NextDoubleValue = 0.0;

            Genome genome = new Genome(randomStub);

            genome.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            genome.AddNode(new Node(Node.ENodeType.OUTPUT, 2));

            genome.AddConnectionGene(1, 2, true);

            int  connectionIndex = -1;
            bool found           = genome.FindConnectionGeneToSplit(out connectionIndex);

            Assert.AreEqual(true, found);
            Assert.AreEqual(0, connectionIndex);
        }
Пример #11
0
        public void TestAddNodeMutation()
        {
            Simulation tmpSim = new Simulation(r, gen1, 1);

            gen1.ParentSimulation = tmpSim;


            List <Innovation> innovations = new List <Innovation>();
            Genome            gen3        = new Genome(r);

            gen3.ParentSimulation = tmpSim;

            gen3.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            gen3.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            gen3.AddNode(new Node(Node.ENodeType.OUTPUT, 3));

            gen3.AddConnectionGene(1, 3, 0.5f);
            gen3.AddConnectionGene(2, 3, 1.0f);

            Assert.IsTrue(gen3.Nodes.Count == 3);
            gen3.AddNodeMutation(innovations);
            Assert.IsTrue(gen3.Nodes.Count == 4);
        }
Пример #12
0
        public void TestAddConnectionMutation()
        {
            List <Innovation> innovations = new List <Innovation>();


            Simulation tmpSim = new Simulation(r, gen1, 1);

            gen1.ParentSimulation = tmpSim;


            Genome gen4 = new Genome(r);

            gen4.ParentSimulation = tmpSim;

            // Create 3 sensors
            gen4.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            gen4.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            gen4.AddNode(new Node(Node.ENodeType.SENSOR, 3));

            // Create 1 output
            gen4.AddNode(new Node(Node.ENodeType.OUTPUT, 4));

            // Create 1 hidden node
            gen4.AddNode(new Node(Node.ENodeType.HIDDEN, 5));

            // Add connections from the paper
            gen4.AddConnectionGene(1, 4);
            gen4.AddConnectionGene(2, 4);
            gen4.AddConnectionGene(3, 4);
            gen4.AddConnectionGene(2, 5);
            gen4.AddConnectionGene(5, 4);

            Assert.IsTrue(gen4.ConnectionGenes.Count == 5);
            gen4.AddConnectionMutation(innovations);
            Assert.IsTrue(gen4.ConnectionGenes.Count == 6);
        }
Пример #13
0
        public void FindFirstNonInputNodeTest()
        {
            Assert.AreEqual(3, gen1.FindFirstNonInputNode());

            // Genome 1
            Genome newGenome = new Genome(r);

            // Create 3 sensors & 1 bias
            newGenome.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            newGenome.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            newGenome.AddNode(new Node(Node.ENodeType.SENSOR, 3));
            newGenome.AddNode(new Node(Node.ENodeType.BIAS, 4));

            // Create 1 output
            newGenome.AddNode(new Node(Node.ENodeType.OUTPUT, 4));

            // Create 1 hidden node
            newGenome.AddNode(new Node(Node.ENodeType.HIDDEN, 5));

            Assert.AreEqual(4, newGenome.FindFirstNonInputNode());
        }
Пример #14
0
        private static void PoleBalanceSingleTest(Random r)
        {
            Genome startGenome = new Genome(r);

            startGenome.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            startGenome.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            startGenome.AddNode(new Node(Node.ENodeType.SENSOR, 3));
            startGenome.AddNode(new Node(Node.ENodeType.SENSOR, 4));
            startGenome.AddNode(new Node(Node.ENodeType.SENSOR, 5));
            startGenome.AddNode(new Node(Node.ENodeType.OUTPUT, 6));
            startGenome.AddNode(new Node(Node.ENodeType.OUTPUT, 7));

            startGenome.AddConnectionGene(1, 6, 0.0f);
            startGenome.AddConnectionGene(2, 6, 0.0f);
            startGenome.AddConnectionGene(3, 6, 0.0f);
            startGenome.AddConnectionGene(4, 6, 0.0f);
            startGenome.AddConnectionGene(5, 6, 0.0f);
            startGenome.AddConnectionGene(1, 7, 0.0f);
            startGenome.AddConnectionGene(2, 7, 0.0f);
            startGenome.AddConnectionGene(3, 7, 0.0f);
            startGenome.AddConnectionGene(4, 7, 0.0f);
            startGenome.AddConnectionGene(5, 7, 0.0f);

            // Run simulation
            Simulation sim          = new Simulation(r, startGenome, 150);
            int        numberOfRuns = 200;
            int        numnodes;       // Used to figure out how many nodes should be visited during activation
            int        thresh;         // How many visits will be allowed before giving up (for loop detection)
            int        MAX_STEPS     = 100000;
            bool       solutionFound = false;
            Genome     bestGenome    = null;

            for (int i = 0; i < numberOfRuns; ++i)
            {
                double epochBestFitness   = 0.0f;
                float  avgConnectionGenes = 0.0f;

                // Evaluate all genomes
                foreach (Genome gen in sim.Genomes)
                {
                    Network network = gen.GetNetwork();
                    numnodes = gen.Nodes.Count;
                    thresh   = numnodes * 2;

                    gen.Fitness = Go_cart(network, MAX_STEPS, thresh, r);
                    if (gen.Fitness > epochBestFitness)
                    {
                        epochBestFitness = gen.Fitness;
                    }
                    avgConnectionGenes += gen.ConnectionGenes.Count;

                    if (gen.Fitness >= MAX_STEPS)
                    {
                        bestGenome    = gen;
                        solutionFound = true;
                        break;
                    }
                }

                avgConnectionGenes /= sim.Genomes.Count;

                Console.WriteLine(String.Format("Epoch {0} | best: {1} | avg genes: {2} | species: {3}", i, epochBestFitness, avgConnectionGenes, sim.Species.Count));

                if (solutionFound)
                {
                    break;
                }

                sim.Epoch();
            }

            if (solutionFound)
            {
                Console.WriteLine(String.Format("Solution network nodes count: {0} | connections count: {1}", bestGenome.Nodes.Count, bestGenome.ConnectionGenes.Count));
            }
            else
            {
                Console.WriteLine("Solution NOT found!");
            }
        }
Пример #15
0
        private static void XorTest(Random r)
        {
            Genome xorGene = new Genome(r);

            xorGene.AddNode(new Node(Node.ENodeType.SENSOR, 1));
            xorGene.AddNode(new Node(Node.ENodeType.SENSOR, 2));
            xorGene.AddNode(new Node(Node.ENodeType.SENSOR, 3));
            xorGene.AddNode(new Node(Node.ENodeType.OUTPUT, 4));

            xorGene.AddConnectionGene(1, 4, 0.0f);
            xorGene.AddConnectionGene(2, 4, 0.0f);
            xorGene.AddConnectionGene(3, 4, 0.0f);

            // Run simulation
            Simulation sim = new Simulation(r, xorGene, 150);

            sim.Parameters.AreConnectionWeightsCapped = false;
            sim.Parameters.MaxWeight           = 1.0f;
            sim.Parameters.WeightMutationPower = 2.5f;

            int    numberOfRuns  = 200;
            bool   solutionFound = false;
            Genome bestGenome    = null;

            float[] output     = { 0.0f, 0.0f, 0.0f, 0.0f };
            float[] bestOutput = new float[4];
            float[,] input =
            {
                { 1.0f, 0.0f, 0.0f },
                { 1.0f, 0.0f, 1.0f },
                { 1.0f, 1.0f, 0.0f },
                { 1.0f, 1.0f, 1.0f }
            };
            for (int i = 0; i < numberOfRuns; ++i)
            {
                double epochBestFitness   = 0.0f;
                float  avgConnectionGenes = 0.0f;

                // Evaluate all genomes
                foreach (Genome gen in sim.Genomes)
                {
                    Network network = gen.GetNetwork();
                    //network.ActivationFunction = new NeuralEvolution.ActivationFunctions.ReLU();
                    bool couldActivate = true;
                    for (int inputIdx = 0; inputIdx < 4; ++inputIdx)
                    {
                        float[] inputRow = new float[3];
                        for (int k = 0; k < 3; ++k)
                        {
                            inputRow[k] = input[inputIdx, k];
                        }
                        network.SetInput(inputRow);
                        if (!network.Activate())
                        {
                            couldActivate = false;
                        }

                        // Relax network
                        int networkDepth = network.GetMaxDepth();
                        for (int relax = 0; relax <= networkDepth; ++relax)
                        {
                            network.Activate();
                        }

                        output[inputIdx] = network.Output[0].Activation;

                        network.Reset();
                    }

                    float errorsum = (float)(Math.Abs(output[0]) + Math.Abs(1.0 - output[1]) + Math.Abs(1.0 - output[2]) + Math.Abs(output[3]));
                    if (!couldActivate)
                    {
                        errorsum = 4;
                    }
                    gen.Fitness = Math.Pow((4.0 - errorsum), 2);
                    gen.Error   = errorsum;

                    if (gen.Fitness > epochBestFitness)
                    {
                        bestOutput[0]    = output[0]; bestOutput[1] = output[1]; bestOutput[2] = output[2]; bestOutput[3] = output[3];
                        epochBestFitness = gen.Fitness;
                    }

                    avgConnectionGenes += gen.ConnectionGenes.Count;

                    if ((output[0] < 0.5f) && (output[1] >= 0.5f) && (output[2] >= 0.5f) && (output[3] < 0.5f))
                    {
                        bestOutput[0] = output[0]; bestOutput[1] = output[1]; bestOutput[2] = output[2]; bestOutput[3] = output[3];
                        bestGenome    = gen;
                        solutionFound = true;
                        break;
                    }
                }

                avgConnectionGenes /= sim.Genomes.Count;

                Console.WriteLine(String.Format("Epoch {0} | best: {1} | best output: [{2}, {3}, {4}, {5}] | avg genes: {6} | species: {7}", i, epochBestFitness, bestOutput[0], bestOutput[1], bestOutput[2], bestOutput[3], avgConnectionGenes, sim.Species.Count));

                // We found a solution so we can exit already
                if (solutionFound)
                {
                    break;
                }

                // Advance to the next step of simulation
                sim.Epoch();
            }

            if (solutionFound)
            {
                Console.WriteLine(String.Format("Solution found: [{0}, {1}, {2}, {3}]", bestOutput[0], bestOutput[1], bestOutput[2], bestOutput[3]));
                Console.WriteLine(String.Format("Solution network nodes count: {0} | connections count: {1}", bestGenome.Nodes.Count, bestGenome.ConnectionGenes.Count));
            }
            else
            {
                Console.WriteLine("Solution NOT found!");
            }
        }
Пример #16
0
        private void InitSimulation()
        {
            Console.WriteLine("Would you like to save all best snakes? [Yn]");
            var key = Console.ReadKey().Key;

            if (key == ConsoleKey.Y)
            {
                this.saveAllBestSnakes = true;
            }
            Console.WriteLine("");
            Console.WriteLine("--- Simulation started ---");

            Snake.StartingLength = 1;            // 3;// int.Parse(this.startingSnakeLength.text);

            Genome startGenome = new Genome(rnd);
            int    startIdx    = 1;
            int    bias        = 0;             // 1 node is used as a bias (always 1.0)
            // 3 nodes are used to store distance to the walls
            // 3 nodes are used to store distance to the snake itself (to avoid collisions with self),
            // 3 nodes are used to store distance to food
            int inputs  = 3 + 3 + 3;
            int hidden  = 0;
            int outputs = 3;                            // 3 outputs. Direction of movement - left, right, straight

            for (int i = 0; i < bias; ++i)
            {
                startGenome.AddNode(new Node(Node.ENodeType.BIAS, startIdx++));
            }

            int inputStart = startIdx;

            for (int i = 0; i < inputs; ++i)
            {
                startGenome.AddNode(new Node(Node.ENodeType.SENSOR, startIdx++));
            }

            int hiddenStart = startIdx;

            for (int i = 0; i < hidden; ++i)
            {
                startGenome.AddNode(new Node(Node.ENodeType.HIDDEN, startIdx++));
            }

            // Output: Move left, straight, right
            int outputStart = startIdx;

            for (int i = 0; i < outputs; ++i)
            {
                startGenome.AddNode(new Node(Node.ENodeType.OUTPUT, startIdx++));
            }

            if (hidden > 0)
            {
                for (int i = 0; i < hidden; ++i)
                {
                    for (int j = 0; j < inputs; ++j)
                    {
                        startGenome.AddConnectionGene(inputStart + j, hiddenStart + i, 0.0f);
                    }
                    for (int j = 0; j < outputs; ++j)
                    {
                        startGenome.AddConnectionGene(hiddenStart + i, outputStart + j, 0.0f);
                    }
                }
            }
            else
            {
                // Walls distance

                /*startGenome.AddConnectionGene(2, outputStart + 0, 0.0f);
                 * startGenome.AddConnectionGene(3, outputStart + 1, 0.0f);
                 * startGenome.AddConnectionGene(4, outputStart + 2, 0.0f);
                 *
                 * // Tails distance
                 * startGenome.AddConnectionGene(5, outputStart + 0, 0.0f);
                 * startGenome.AddConnectionGene(6, outputStart + 1, 0.0f);
                 * startGenome.AddConnectionGene(7, outputStart + 2, 0.0f);
                 *
                 * // Food distance
                 * startGenome.AddConnectionGene(8, outputStart + 0, 0.0f);
                 * startGenome.AddConnectionGene(9, outputStart + 1, 0.0f);
                 * startGenome.AddConnectionGene(10, outputStart + 2, 0.0f);*/
                for (int i = 0; i < inputs; ++i)
                {
                    //for(int j = 0; j < outputs; ++j)
                    //startGenome.AddConnectionGene(inputStart + i, outputStart + j, 0.0f);
                    startGenome.AddConnectionGene(inputStart + i, outputStart + rnd.Next(outputs), 0.0f);
                }
            }

            // Connect bias node to every output
            for (int i = 0; i < bias; ++i)
            {
                for (int j = 0; j < outputs; ++j)
                {
                    startGenome.AddConnectionGene(i + 1, outputStart + j, 0.0f);
                }
            }


            // Set up simulation - but don't start it
            sim = new Simulation(rnd, startGenome, 15000);
            sim.Parameters.AddNodeProbability                 = 0.02f;
            sim.Parameters.AddConnectionProbability           = 0.1f;
            sim.Parameters.WeightMutationPower                = 1.0f;
            sim.Parameters.MutateConnectionWeightsProbability = 0.8f;
            sim.Parameters.MutateToggleEnabledProbability     = 0.05f;
            sim.Parameters.MutateReenableProbability          = 0.025f;
            sim.Parameters.SurvivalThreshold          = 0.01f;
            sim.Parameters.AreConnectionWeightsCapped = true;
            sim.Parameters.MaxWeight = 1.0f;
            sim.Parameters.MaxSpeciesGenerationsWithoutImprovement = 70;
            sim.Parameters.MaxGeneralGenerationsWithoutImprovement = 80;
        }
Пример #17
0
        public Generation MakeFirstGeneration(InnovationGenerator generator, InnovationGenerator genomeGenerator,
                                              int initialPopulationSize, int selectionAlgorithm, int reproductionsPerGenome, int nBest)
        {
            // check if there is a Lua implementation of this
            LuaFunction func = LuaState["MakeFirstGeneration"] as LuaFunction;

            if (func != null)
            {
                try
                {
                    return((Generation)func.Call(generator, initialPopulationSize)?.First());
                }
                catch (Exception e)
                {
                    logger.Error(e, "Error running lua code for first generation.");
                }
            }

            Generation generation = new Generation(0, selectionAlgorithm, reproductionsPerGenome, nBest);
            Genome     genome     = new Genome(0);

            int inputs  = Data.Constants.NetworkInputs;
            int outputs = Data.Constants.NetworkOutputs;

            List <int> inputIds = new List <int>(inputs);

            // input nodes
            for (int i = 0; i < inputs; i++)
            {
                int currentId = generator.Innovate();
                inputIds.Add(currentId);
                genome.AddNode(new Node(currentId, NodeType.Input, int.MinValue));
            }

            // output nodes
            for (int i = 0; i < outputs; i++)
            {
                int currentId = generator.Innovate();
                genome.AddNode(new Node(currentId, NodeType.Output, int.MaxValue));

                foreach (int hiddenId in inputIds)
                {
                    genome.AddConnection(new Connection(hiddenId, currentId, Double() * 4f - 2f, true, generator.Innovate()));
                }
            }

            Species original = new Species(genome);

            generation.Species.Add(original);

            for (int i = 0; i < initialPopulationSize; i++)
            {
                Genome g = genome.Copy();
                g.Mutate(generator);
                g.Id = genomeGenerator.Innovate();

                original.AddGenome(g);
            }

            return(generation);
        }