Пример #1
0
        /// <summary>
        ///  Construct a genome by copying another.
        /// </summary>
        /// <param name="other">The other genome.</param>
        public NEATGenome(NEATGenome other)
        {
            NetworkDepth  = other.NetworkDepth;
            Population    = other.Population;
            Score         = other.Score;
            AdjustedScore = other.AdjustedScore;
            InputCount    = other.InputCount;
            OutputCount   = other.OutputCount;
            Species       = other.Species;

            // copy neurons
            foreach (NEATNeuronGene oldGene in other.NeuronsChromosome)
            {
                var newGene = new NEATNeuronGene(oldGene);
                _neuronsList.Add(newGene);
            }

            // copy links
            foreach (var oldGene in other.LinksChromosome)
            {
                var newGene = new NEATLinkGene(
                    oldGene.FromNeuronId, oldGene.ToNeuronId,
                    oldGene.Enabled, oldGene.InnovationId,
                    oldGene.Weight);
                _linksList.Add(newGene);
            }
        }
Пример #2
0
        /// <summary>
        /// Validate the structure of this genome.
        /// </summary>
        public void Validate()
        {
            // make sure that the bias neuron is where it should be
            NEATNeuronGene g = _neuronsList[0];

            if (g.NeuronType != NEATNeuronType.Bias)
            {
                throw new EncogError("NEAT Neuron Gene 0 should be a bias gene.");
            }

            // make sure all input neurons are at the beginning
            for (int i = 1; i <= InputCount; i++)
            {
                NEATNeuronGene gene = _neuronsList[i];
                if (gene.NeuronType != NEATNeuronType.Input)
                {
                    throw new EncogError("NEAT Neuron Gene " + i
                                         + " should be an input gene.");
                }
            }

            // make sure that there are no double links
            IDictionary <String, NEATLinkGene> map = new Dictionary <String, NEATLinkGene>();

            foreach (NEATLinkGene nlg in _linksList)
            {
                String key = nlg.FromNeuronId + "->" + nlg.ToNeuronId;
                if (map.ContainsKey(key))
                {
                    throw new EncogError("Double link found: " + key);
                }
                map[key] = nlg;
            }
        }
Пример #3
0
        /// <summary>
        /// Copy another gene to this one.
        /// </summary>
        /// <param name="gene">The other gene.</param>
        public void Copy(NEATNeuronGene gene)
        {
            NEATNeuronGene other = gene;

            Id                 = other.Id;
            NeuronType         = other.NeuronType;
            ActivationFunction = other.ActivationFunction;
            InnovationId       = other.InnovationId;
        }
Пример #4
0
        /// <summary>
        /// Create a new genome with the specified connection density. This
        /// constructor is typically used to create the initial population.
        /// </summary>
        /// <param name="rnd">Random number generator.</param>
        /// <param name="pop">The population.</param>
        /// <param name="inputCount">The input count.</param>
        /// <param name="outputCount">The output count.</param>
        /// <param name="connectionDensity">The connection density.</param>
        public NEATGenome(EncogRandom rnd, NEATPopulation pop,
                          int inputCount, int outputCount,
                          double connectionDensity)
        {
            AdjustedScore = 0;
            InputCount    = inputCount;
            OutputCount   = outputCount;

            // get the activation function
            IActivationFunction af = pop.ActivationFunctions.PickFirst();

            // first bias
            int innovationId = 0;
            var biasGene     = new NEATNeuronGene(NEATNeuronType.Bias, af,
                                                  inputCount, innovationId++);

            _neuronsList.Add(biasGene);

            // then inputs

            for (var i = 0; i < inputCount; i++)
            {
                var gene = new NEATNeuronGene(NEATNeuronType.Input, af,
                                              i, innovationId++);
                _neuronsList.Add(gene);
            }

            // then outputs

            for (int i = 0; i < outputCount; i++)
            {
                var gene = new NEATNeuronGene(NEATNeuronType.Output, af,
                                              i + inputCount + 1, innovationId++);
                _neuronsList.Add(gene);
            }

            // and now links
            for (var i = 0; i < inputCount + 1; i++)
            {
                for (var j = 0; j < outputCount; j++)
                {
                    // make sure we have at least one connection
                    if (_linksList.Count < 1 ||
                        rnd.NextDouble() < connectionDensity)
                    {
                        long   fromId = this._neuronsList[i].Id;
                        long   toId   = this._neuronsList[inputCount + j + 1].Id;
                        double w      = RangeRandomizer.Randomize(rnd, -pop.WeightRange, pop.WeightRange);
                        var    gene   = new NEATLinkGene(fromId, toId, true,
                                                         innovationId++, w);
                        _linksList.Add(gene);
                    }
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Construct an innovation.
        /// </summary>
        ///
        /// <param name="neuronGene">The neuron gene.</param>
        /// <param name="innovationID">The innovation id.</param>
        /// <param name="neuronID_0">The neuron id.</param>
        public NEATInnovation(NEATNeuronGene neuronGene,
                              long innovationID, long neuronID_0)
        {
            neuronID     = neuronID_0;
            InnovationID = innovationID;
            splitX       = neuronGene.SplitX;
            splitY       = neuronGene.SplitY;

            neuronType     = neuronGene.NeuronType;
            innovationType = NEATInnovationType.NewNeuron;
            fromNeuronID   = -1;
            toNeuronID     = -1;
        }
Пример #6
0
        /// <summary>
        /// Construct a genome by copying another.
        /// </summary>
        ///
        /// <param name="other">The other genome.</param>
        public NEATGenome(NEATGenome other)
        {
            neuronsChromosome = new Chromosome();
            linksChromosome   = new Chromosome();
            GA = other.GA;

            Chromosomes.Add(neuronsChromosome);
            Chromosomes.Add(linksChromosome);

            GenomeID      = other.GenomeID;
            networkDepth  = other.networkDepth;
            Population    = other.Population;
            Score         = other.Score;
            AdjustedScore = other.AdjustedScore;
            AmountToSpawn = other.AmountToSpawn;
            inputCount    = other.inputCount;
            outputCount   = other.outputCount;
            speciesID     = other.speciesID;


            // copy neurons
            foreach (IGene gene  in  other.Neurons.Genes)
            {
                var oldGene = (NEATNeuronGene)gene;
                var newGene = new NEATNeuronGene(
                    oldGene.NeuronType, oldGene.Id,
                    oldGene.SplitY, oldGene.SplitX,
                    oldGene.Recurrent, oldGene.ActivationResponse);
                Neurons.Add(newGene);
            }


            // copy links
            foreach (IGene gene_0  in  other.Links.Genes)
            {
                var oldGene_1 = (NEATLinkGene)gene_0;
                var newGene_2 = new NEATLinkGene(
                    oldGene_1.FromNeuronID, oldGene_1.ToNeuronID,
                    oldGene_1.Enabled, oldGene_1.InnovationId,
                    oldGene_1.Weight, oldGene_1.Recurrent);
                Links.Add(newGene_2);
            }
        }
        /// <summary>
        /// Create a new neuron gene from an id.
        /// </summary>
        ///
        /// <param name="neuronID">The neuron id.</param>
        /// <returns>The neuron gene.</returns>
        public NEATNeuronGene CreateNeuronFromID(long neuronID)
        {
            var result = new NEATNeuronGene(NEATNeuronType.Hidden,
                                            0, 0, 0);


            foreach (IInnovation i  in  Innovations)
            {
                var innovation = (NEATInnovation)i;
                if (innovation.NeuronID == neuronID)
                {
                    result.NeuronType = innovation.NeuronType;
                    result.Id         = innovation.NeuronID;
                    result.SplitY     = innovation.SplitY;
                    result.SplitX     = innovation.SplitX;

                    return(result);
                }
            }

            throw new TrainingError("Failed to find innovation for neuron: "
                                    + neuronID);
        }
        /// <inheritdoc/>
        public Object Read(Stream istream)
        {
            long nextInnovationId = 0;
            long nextGeneId = 0;

            var result = new NEATPopulation();
            var innovationList = new NEATInnovationList {Population = result};
            result.Innovations = innovationList;
            var reader = new EncogReadHelper(istream);
            EncogFileSection section;

            while ((section = reader.ReadNextSection()) != null)
            {
                if (section.SectionName.Equals("NEAT-POPULATION")
                        && section.SubSectionName.Equals("INNOVATIONS"))
                {
                    foreach (String line in section.Lines)
                    {
                        IList<String> cols = EncogFileSection
                                .SplitColumns(line);
                        var innovation = new NEATInnovation();
                        var innovationId = int.Parse(cols[1]);
                        innovation.InnovationId = innovationId;
                        innovation.NeuronId = int.Parse(cols[2]);
                        result.Innovations.Innovations[cols[0]] = innovation;
                        nextInnovationId = Math.Max(nextInnovationId, innovationId + 1);
                    }
                }
                else if (section.SectionName.Equals("NEAT-POPULATION")
                      && section.SubSectionName.Equals("SPECIES"))
                {
                    NEATGenome lastGenome = null;
                    BasicSpecies lastSpecies = null;

                    foreach (String line in section.Lines)
                    {
                        IList<String> cols = EncogFileSection.SplitColumns(line);

                        if (String.Compare(cols[0], "s", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            lastSpecies = new BasicSpecies
                                {
                                    Population = result,
                                    Age = int.Parse(cols[1]),
                                    BestScore = CSVFormat.EgFormat.Parse(cols[2]),
                                    GensNoImprovement = int.Parse(cols[3])
                                };
                            result.Species.Add(lastSpecies);
                        }
                        else if (String.Compare(cols[0], "g", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            bool isLeader = lastGenome == null;
                            lastGenome = new NEATGenome
                                {
                                    InputCount = result.InputCount,
                                    OutputCount = result.OutputCount,
                                    Species = lastSpecies,
                                    AdjustedScore = CSVFormat.EgFormat.Parse(cols[1]),
                                    Score = CSVFormat.EgFormat.Parse(cols[2]),
                                    BirthGeneration = int.Parse(cols[3])
                                };
                            lastSpecies.Add(lastGenome);
                            if (isLeader)
                            {
                                lastSpecies.Leader = lastGenome;
                            }
                        }
                        else if (String.Compare(cols[0], "n", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            var neuronGene = new NEATNeuronGene();
                            int geneId = int.Parse(cols[1]);
                            neuronGene.Id = geneId;

                            IActivationFunction af = EncogFileSection.ParseActivationFunction(cols[2]);
                            neuronGene.ActivationFunction = af;

                            neuronGene.NeuronType = PersistNEATPopulation.StringToNeuronType(cols[3]);
                            neuronGene.InnovationId = int.Parse(cols[4]);
                            lastGenome.NeuronsChromosome.Add(neuronGene);
                            nextGeneId = Math.Max(geneId + 1, nextGeneId);
                        }
                        else if (String.Compare(cols[0], "l", StringComparison.OrdinalIgnoreCase) == 0)
                        {
                            var linkGene = new NEATLinkGene
                                {
                                    Id = int.Parse(cols[1]),
                                    Enabled = (int.Parse(cols[2]) > 0),
                                    FromNeuronId = int.Parse(cols[3]),
                                    ToNeuronId = int.Parse(cols[4]),
                                    Weight = CSVFormat.EgFormat.Parse(cols[5]),
                                    InnovationId = int.Parse(cols[6])
                                };
                            lastGenome.LinksChromosome.Add(linkGene);
                        }
                    }

                }
                else if (section.SectionName.Equals("NEAT-POPULATION")
                      && section.SubSectionName.Equals("CONFIG"))
                {
                    IDictionary<string, string> prm = section.ParseParams();

                    string afStr = prm[NEATPopulation.PropertyNEATActivation];

                    if (String.Compare(afStr, TypeCppn, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        HyperNEATGenome.BuildCPPNActivationFunctions(result.ActivationFunctions);
                    }
                    else
                    {
                        result.NEATActivationFunction = EncogFileSection.ParseActivationFunction(prm,
                                        NEATPopulation.PropertyNEATActivation);
                    }

                    result.ActivationCycles = EncogFileSection.ParseInt(prm,
                            PersistConst.ActivationCycles);
                    result.InputCount = EncogFileSection.ParseInt(prm,
                            PersistConst.InputCount);
                    result.OutputCount = EncogFileSection.ParseInt(prm,
                            PersistConst.OutputCount);
                    result.PopulationSize = EncogFileSection.ParseInt(prm,
                            NEATPopulation.PropertyPopulationSize);
                    result.SurvivalRate = EncogFileSection.ParseDouble(prm,
                            NEATPopulation.PropertySurvivalRate);
                    result.ActivationCycles = EncogFileSection.ParseInt(prm,
                            NEATPopulation.PropertyCycles);
                }
            }

            // set factories
            if (result.IsHyperNEAT)
            {
                result.GenomeFactory = new FactorHyperNEATGenome();
                result.CODEC = new HyperNEATCODEC();
            }
            else
            {
                result.GenomeFactory = new FactorNEATGenome();
                result.CODEC = new NEATCODEC();
            }

            // set the next ID's
            result.InnovationIDGenerate.CurrentID = nextInnovationId;
            result.GeneIdGenerate.CurrentID = nextGeneId;

            // find first genome, which should be the best genome
            if (result.Species.Count > 0)
            {
                ISpecies species = result.Species[0];
                if (species.Members.Count > 0)
                {
                    result.BestGenome = species.Members[0];
                }
            }

            return result;
        }
Пример #9
0
 /// <summary>
 /// Copy another gene to this one.
 /// </summary>
 /// <param name="gene">The other gene.</param>
 public void Copy(NEATNeuronGene gene)
 {
     NEATNeuronGene other = gene;
     Id = other.Id;
     NeuronType = other.NeuronType;
     ActivationFunction = other.ActivationFunction;
     InnovationId = other.InnovationId;
 }
Пример #10
0
 /// <summary>
 /// Construct this gene by comping another.
 /// </summary>
 /// <param name="other">The other gene to copy.</param>
 public NEATNeuronGene(NEATNeuronGene other)
 {
     Copy(other);
 }
Пример #11
0
        /// <summary>
        /// Mutate the genome by adding a neuron.
        /// </summary>
        ///
        /// <param name="mutationRate">The mutation rate.</param>
        /// <param name="numTrysToFindOldLink">The number of tries to find a link to split.</param>
        internal void AddNeuron(double mutationRate, int numTrysToFindOldLink)
        {
            // should we add a neuron?
            if (ThreadSafeRandom.NextDouble() > mutationRate)
            {
                return;
            }

            int countTrysToFindOldLink = numTrysToFindOldLink;

            // the link to split
            NEATLinkGene splitLink = null;

            int sizeBias = inputCount + outputCount + 10;

            // if there are not at least
            int upperLimit;

            if (linksChromosome.Size() < sizeBias)
            {
                upperLimit = NumGenes - 1 - (int)Math.Sqrt(NumGenes);
            }
            else
            {
                upperLimit = NumGenes - 1;
            }

            while ((countTrysToFindOldLink--) > 0)
            {
                // choose a link, use the square root to prefer the older links
                int i    = RangeRandomizer.RandomInt(0, upperLimit);
                var link = (NEATLinkGene)linksChromosome
                           .Get(i);

                // get the from neuron
                long fromNeuron = link.FromNeuronID;

                if ((link.Enabled) &&
                    (!link.Recurrent) &&
                    (((NEATNeuronGene)Neurons.Get(
                          GetElementPos(fromNeuron))).NeuronType != NEATNeuronType.Bias))
                {
                    splitLink = link;
                    break;
                }
            }

            if (splitLink == null)
            {
                return;
            }

            splitLink.Enabled = false;

            double originalWeight = splitLink.Weight;

            long from = splitLink.FromNeuronID;
            long to   = splitLink.ToNeuronID;

            var fromGene = (NEATNeuronGene)Neurons.Get(
                GetElementPos(from));
            var toGene = (NEATNeuronGene)Neurons.Get(
                GetElementPos(to));

            double newDepth = (fromGene.SplitY + toGene.SplitY) / 2;
            double newWidth = (fromGene.SplitX + toGene.SplitX) / 2;

            // has this innovation already been tried?
            NEATInnovation innovation = ((NEATTraining)GA).Innovations.CheckInnovation(from, to,
                                                                                       NEATInnovationType
                                                                                       .NewNeuron);

            // prevent chaining
            if (innovation != null)
            {
                long neuronID = innovation.NeuronID;

                if (AlreadyHaveThisNeuronID(neuronID))
                {
                    innovation = null;
                }
            }

            if (innovation == null)
            {
                // this innovation has not been tried, create it
                long newNeuronID = ((NEATTraining)GA).Innovations.CreateNewInnovation(from, to,
                                                                                      NEATInnovationType.
                                                                                      NewNeuron,
                                                                                      NEATNeuronType.
                                                                                      Hidden,
                                                                                      newWidth, newDepth);

                neuronsChromosome.Add(new NEATNeuronGene(
                                          NEATNeuronType.Hidden, newNeuronID, newDepth, newWidth));

                // add the first link
                long link1ID = (GA).Population.AssignInnovationID();

                ((NEATTraining)GA).Innovations
                .CreateNewInnovation(from, newNeuronID,
                                     NEATInnovationType.NewLink);

                var link1 = new NEATLinkGene(from, newNeuronID,
                                             true, link1ID, 1.0d, false);

                linksChromosome.Add(link1);

                // add the second link
                long link2ID = (GA).Population.AssignInnovationID();

                ((NEATTraining)GA).Innovations
                .CreateNewInnovation(newNeuronID, to,
                                     NEATInnovationType.NewLink);

                var link2 = new NEATLinkGene(newNeuronID, to, true,
                                             link2ID, originalWeight, false);

                linksChromosome.Add(link2);
            }

            else
            {
                // existing innovation
                long newNeuronID_0 = innovation.NeuronID;

                NEATInnovation innovationLink1 = ((NEATTraining)GA).Innovations.CheckInnovation(from,
                                                                                                newNeuronID_0,
                                                                                                NEATInnovationType
                                                                                                .
                                                                                                NewLink);
                NEATInnovation innovationLink2 =
                    ((NEATTraining)GA).Innovations.CheckInnovation(newNeuronID_0, to,
                                                                   NEATInnovationType.NewLink);

                if ((innovationLink1 == null) || (innovationLink2 == null))
                {
                    throw new NeuralNetworkError("NEAT Error");
                }

                var link1_1 = new NEATLinkGene(from, newNeuronID_0,
                                               true, innovationLink1.InnovationID, 1.0d, false);
                var link2_2 = new NEATLinkGene(newNeuronID_0, to, true,
                                               innovationLink2.InnovationID, originalWeight, false);

                linksChromosome.Add(link1_1);
                linksChromosome.Add(link2_2);

                var newNeuron = new NEATNeuronGene(
                    NEATNeuronType.Hidden, newNeuronID_0, newDepth, newWidth);

                neuronsChromosome.Add(newNeuron);
            }

            return;
        }
Пример #12
0
        /// <summary>
        /// Mutate the genome by adding a link to this genome.
        /// </summary>
        ///
        /// <param name="mutationRate">The mutation rate.</param>
        /// <param name="chanceOfLooped">The chance of a self-connected neuron.</param>
        /// <param name="numTrysToFindLoop">The number of tries to find a loop.</param>
        /// <param name="numTrysToAddLink">The number of tries to add a link.</param>
        internal void AddLink(double mutationRate, double chanceOfLooped,
                              int numTrysToFindLoop, int numTrysToAddLink)
        {
            // should we even add the link
            if (ThreadSafeRandom.NextDouble() > mutationRate)
            {
                return;
            }

            int countTrysToFindLoop = numTrysToFindLoop;
            int countTrysToAddLink  = numTrysToFindLoop;

            // the link will be between these two neurons
            long neuron1ID = -1;
            long neuron2ID = -1;

            bool recurrent = false;

            // a self-connected loop?
            if (ThreadSafeRandom.NextDouble() < chanceOfLooped)
            {
                // try to find(randomly) a neuron to add a self-connected link to
                while ((countTrysToFindLoop--) > 0)
                {
                    NEATNeuronGene neuronGene = ChooseRandomNeuron(false);

                    // no self-links on input or bias neurons
                    if (!neuronGene.Recurrent &&
                        (neuronGene.NeuronType != NEATNeuronType.Bias) &&
                        (neuronGene.NeuronType != NEATNeuronType.Input))
                    {
                        neuron1ID = neuronGene.Id;
                        neuron2ID = neuronGene.Id;

                        neuronGene.Recurrent = true;
                        recurrent            = true;

                        countTrysToFindLoop = 0;
                    }
                }
            }
            else
            {
                // try to add a regular link
                while ((countTrysToAddLink--) > 0)
                {
                    NEATNeuronGene neuron1 = ChooseRandomNeuron(true);
                    NEATNeuronGene neuron2 = ChooseRandomNeuron(false);

                    if (!IsDuplicateLink(neuron1ID, neuron2ID) &&
                        (neuron1.Id != neuron2.Id) &&
                        (neuron2.NeuronType != NEATNeuronType.Bias))
                    {
                        neuron1ID = neuron1.Id;
                        neuron2ID = neuron2.Id;
                        break;
                    }
                }
            }

            // did we fail to find a link
            if ((neuron1ID < 0) || (neuron2ID < 0))
            {
                return;
            }

            // check to see if this innovation has already been tried
            NEATInnovation innovation = ((NEATTraining)GA).Innovations.CheckInnovation(neuron1ID,
                                                                                       neuron1ID,
                                                                                       NEATInnovationType
                                                                                       .NewLink);

            // see if this is a recurrent(backwards) link
            var neuronGene_0 = (NEATNeuronGene)neuronsChromosome
                               .Get(GetElementPos(neuron1ID));

            if (neuronGene_0.SplitY > neuronGene_0.SplitY)
            {
                recurrent = true;
            }

            // is this a new innovation?
            if (innovation == null)
            {
                // new innovation
                ((NEATTraining)GA).Innovations
                .CreateNewInnovation(neuron1ID, neuron2ID,
                                     NEATInnovationType.NewLink);

                long id2 = GA.Population.AssignInnovationID();

                var linkGene = new NEATLinkGene(neuron1ID,
                                                neuron2ID, true, id2, RangeRandomizer.Randomize(-1, 1),
                                                recurrent);
                linksChromosome.Add(linkGene);
            }
            else
            {
                // existing innovation
                var linkGene_1 = new NEATLinkGene(neuron1ID,
                                                  neuron2ID, true, innovation.InnovationID,
                                                  RangeRandomizer.Randomize(-1, 1), recurrent);
                linksChromosome.Add(linkGene_1);
            }
        }
Пример #13
0
 /// <summary>
 /// Construct this gene by comping another.
 /// </summary>
 /// <param name="other">The other gene to copy.</param>
 public NEATNeuronGene(NEATNeuronGene other)
 {
     Copy(other);
 }
        /// <summary>
        ///  Construct a genome by copying another. 
        /// </summary>
        /// <param name="other">The other genome.</param>
        public NEATGenome(NEATGenome other)
        {
            NetworkDepth = other.NetworkDepth;
            Population = other.Population;
            Score = other.Score;
            AdjustedScore = other.AdjustedScore;
            InputCount = other.InputCount;
            OutputCount = other.OutputCount;
            Species = other.Species;

            // copy neurons
            foreach (NEATNeuronGene oldGene in other.NeuronsChromosome)
            {
                var newGene = new NEATNeuronGene(oldGene);
                _neuronsList.Add(newGene);
            }

            // copy links
            foreach (var oldGene in other.LinksChromosome)
            {
                var newGene = new NEATLinkGene(
                        oldGene.FromNeuronId, oldGene.ToNeuronId,
                        oldGene.Enabled, oldGene.InnovationId,
                        oldGene.Weight);
                _linksList.Add(newGene);
            }
        }
        /// <summary>
        /// Create a new genome with the specified connection density. This
        /// constructor is typically used to create the initial population.
        /// </summary>
        /// <param name="rnd">Random number generator.</param>
        /// <param name="pop">The population.</param>
        /// <param name="inputCount">The input count.</param>
        /// <param name="outputCount">The output count.</param>
        /// <param name="connectionDensity">The connection density.</param>
        public NEATGenome(EncogRandom rnd, NEATPopulation pop,
            int inputCount, int outputCount,
            double connectionDensity)
        {
            AdjustedScore = 0;
            InputCount = inputCount;
            OutputCount = outputCount;

            // get the activation function
            IActivationFunction af = pop.ActivationFunctions.PickFirst();

            // first bias
            int innovationId = 0;
            var biasGene = new NEATNeuronGene(NEATNeuronType.Bias, af,
                    inputCount, innovationId++);
            _neuronsList.Add(biasGene);

            // then inputs

            for (var i = 0; i < inputCount; i++)
            {
                var gene = new NEATNeuronGene(NEATNeuronType.Input, af,
                        i, innovationId++);
                _neuronsList.Add(gene);
            }

            // then outputs

            for (int i = 0; i < outputCount; i++)
            {
                var gene = new NEATNeuronGene(NEATNeuronType.Output, af,
                        i + inputCount + 1, innovationId++);
                _neuronsList.Add(gene);
            }

            // and now links
            for (var i = 0; i < inputCount + 1; i++)
            {
                for (var j = 0; j < outputCount; j++)
                {
                    // make sure we have at least one connection
                    if (_linksList.Count < 1
                            || rnd.NextDouble() < connectionDensity)
                    {
                        long fromId = this._neuronsList[i].Id;
                        long toId = this._neuronsList[inputCount + j + 1].Id;
                        double w = RangeRandomizer.Randomize(rnd, -pop.WeightRange, pop.WeightRange);
                        var gene = new NEATLinkGene(fromId, toId, true,
                                innovationId++, w);
                        _linksList.Add(gene);
                    }
                }
            }
        }