Stores a NEAT innovation. This is an improvement that was attempted to the neural network.
Inheritance: Encog.Solve.Genetic.Innovation.BasicInnovation
        /// <summary>
        /// Construct an innovation list. 
        /// </summary>
        /// <param name="population">The population.</param>
        /// <param name="links">The links.</param>
        /// <param name="neurons">The neurons.</param>
        public NEATInnovationList(IPopulation population, Chromosome links, Chromosome neurons)
        {

            this.population = population;
            foreach (IGene gene in neurons.Genes)
            {
                NEATNeuronGene neuronGene = (NEATNeuronGene)gene;

                NEATInnovation innovation = new NEATInnovation(neuronGene,
                       population.AssignInnovationID(), AssignNeuronID());

                Innovations.Add(innovation);

            }

            foreach (IGene gene in links.Genes)
            {
                NEATLinkGene linkGene = (NEATLinkGene)gene;
                NEATInnovation innovation = new NEATInnovation(linkGene
                        .FromNeuronID, linkGene.ToNeuronID,
                        NEATInnovationType.NewLink, this.population.AssignInnovationID());
                Innovations.Add(innovation);

            }
        }
        /// <summary>
        /// Create a new innovation.
        /// </summary>
        /// <param name="input">The input neuron.</param>
        /// <param name="output">The output neuron.</param>
        /// <param name="type">The type.</param>
        public void CreateNewInnovation(long input, long output,
                                        NEATInnovationType type)
        {
            NEATInnovation newInnovation = new NEATInnovation(input, output, type,
                                                              this.population.AssignInnovationID());

            if (type == NEATInnovationType.NewNeuron)
            {
                newInnovation.NeuronID = AssignNeuronID();
            }

            Innovations.Add(newInnovation);
        }
        /// <summary>
        /// Create a new innovation.
        /// </summary>
        /// <param name="from">The from neuron.</param>
        /// <param name="to">The to neuron.</param>
        /// <param name="innovationType">The innovation type.</param>
        /// <param name="neuronType">The neuron type.</param>
        /// <param name="x">The x-coordinate.</param>
        /// <param name="y">The y-coordinate.</param>
        /// <returns>The new innovation.</returns>
        public long CreateNewInnovation(long from, long to,
                                        NEATInnovationType innovationType,
                                        NEATNeuronType neuronType, double x, double y)
        {
            NEATInnovation newInnovation = new NEATInnovation(from, to,
                                                              innovationType, population.AssignInnovationID(), neuronType, x, y);

            if (innovationType == NEATInnovationType.NewNeuron)
            {
                newInnovation.NeuronID = AssignNeuronID();
            }

            Innovations.Add(newInnovation);

            return(this.nextNeuronID - 1);  // ??????? should it be innov?
        }
        /// <summary>
        /// Check to see if we already have an innovation.
        /// </summary>
        /// <param name="input">The input neuron.</param>
        /// <param name="output">The output neuron.</param>
        /// <param name="type">The type.</param>
        /// <returns>The innovation, either new or existing if found.</returns>
        public NEATInnovation CheckInnovation(long input, long output,
                                              NEATInnovationType type)
        {
            foreach (IInnovation i in Innovations)
            {
                NEATInnovation innovation = (NEATInnovation)i;
                if ((innovation.FromNeuronID == input) &&
                    (innovation.ToNeuronID == output) &&
                    (innovation.InnovationType == type))
                {
                    return(innovation);
                }
            }

            return(null);
        }
        /// <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)
        {
            NEATNeuronGene result = new NEATNeuronGene(NEATNeuronType.Hidden,
                                                       0, 0, 0);

            foreach (IInnovation i in Innovations)
            {
                NEATInnovation 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);
                }
            }

            return(result);
        }
        /// <summary>
        /// Construct an innovation list.
        /// </summary>
        /// <param name="population">The population.</param>
        /// <param name="links">The links.</param>
        /// <param name="neurons">The neurons.</param>
        public NEATInnovationList(IPopulation population, Chromosome links, Chromosome neurons)
        {
            this.population = population;
            foreach (IGene gene in neurons.Genes)
            {
                NEATNeuronGene neuronGene = (NEATNeuronGene)gene;

                NEATInnovation innovation = new NEATInnovation(neuronGene,
                                                               population.AssignInnovationID(), AssignNeuronID());

                Innovations.Add(innovation);
            }

            foreach (IGene gene in links.Genes)
            {
                NEATLinkGene   linkGene   = (NEATLinkGene)gene;
                NEATInnovation innovation = new NEATInnovation(linkGene
                                                               .FromNeuronID, linkGene.ToNeuronID,
                                                               NEATInnovationType.NewLink, this.population.AssignInnovationID());
                Innovations.Add(innovation);
            }
        }
        /// <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>
        public void AddNeuron(double mutationRate, int numTrysToFindOldLink)
        {
            // should we add a neuron?
            if (ThreadSafeRandom.NextDouble() > mutationRate)
            {
                return;
            }

            // the link to split
            NEATLinkGene splitLink = null;

            int sizeThreshold = inputCount + outputCount + 10;

            // if there are not at least
            int upperLimit;

            if (linksChromosome.Genes.Count < sizeThreshold)
            {
                upperLimit = NumGenes - 1 - (int)Math.Sqrt(NumGenes);
            }
            else
            {
                upperLimit = NumGenes - 1;
            }

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

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

                if ((link.Enabled) &&
                    (!link.IsRecurrent) &&
                    (((NEATNeuronGene)Neurons.Genes[
                          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;

            NEATNeuronGene fromGene = (NEATNeuronGene)Neurons.Genes[
                GetElementPos(from)];
            NEATNeuronGene toGene = (NEATNeuronGene)Neurons.Genes[
                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.Genes.Add(new NEATNeuronGene(NEATNeuronType.Hidden,
                                                               newNeuronID, newDepth, newWidth));

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

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

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

                linksChromosome.Genes.Add(link1);

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

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

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

                linksChromosome.Genes.Add(link2);
            }

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

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

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

                NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID,
                                                      true, innovationLink1.InnovationID, 1.0, false);
                NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true,
                                                      innovationLink2.InnovationID, originalWeight, false);

                linksChromosome.Genes.Add(link1);
                linksChromosome.Genes.Add(link2);

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

                neuronsChromosome.Genes.Add(newNeuron);
            }

            return;
        }
        /// <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>
        public void AddLink(double mutationRate, double chanceOfLooped,
                            int numTrysToFindLoop, int numTrysToAddLink)
        {
            // should we even add the link
            if (ThreadSafeRandom.NextDouble() > mutationRate)
            {
                return;
            }

            // 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 ((numTrysToFindLoop--) > 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 = neuron2ID = neuronGene.Id;

                        neuronGene.Recurrent = true;
                        recurrent            = true;

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

                    if (!IsDuplicateLink(neuron1ID, neuron2ID) &&
                        (neuron1.Id != neuron2.Id) &&
                        (neuron2.NeuronType != NEATNeuronType.Bias))
                    {
                        neuron1ID = -1;
                        neuron2ID = -1;
                        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
            NEATNeuronGene neuronGene2 = (NEATNeuronGene)neuronsChromosome.Genes
                                         [GetElementPos(neuron1ID)];

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

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

                long id2 = ((NEATTraining)GA).Population.AssignInnovationID();

                NEATLinkGene linkGene = new NEATLinkGene(neuron1ID,
                                                         neuron2ID, true, id2, RangeRandomizer.Randomize(-1, 1),
                                                         recurrent);
                linksChromosome.Genes.Add(linkGene);
            }
            else
            {
                // existing innovation
                NEATLinkGene linkGene = new NEATLinkGene(neuron1ID,
                                                         neuron2ID, true, innovation.InnovationID,
                                                         RangeRandomizer.Randomize(-1, 1), recurrent);
                linksChromosome.Genes.Add(linkGene);
            }
        }
        /// <summary>
        /// Create a new innovation. 
        /// </summary>
        /// <param name="from">The from neuron.</param>
        /// <param name="to">The to neuron.</param>
        /// <param name="innovationType">The innovation type.</param>
        /// <param name="neuronType">The neuron type.</param>
        /// <param name="x">The x-coordinate.</param>
        /// <param name="y">The y-coordinate.</param>
        /// <returns>The new innovation.</returns>
        public long CreateNewInnovation(long from, long to,
                NEATInnovationType innovationType,
                NEATNeuronType neuronType, double x, double y)
        {
            NEATInnovation newInnovation = new NEATInnovation(from, to,
                    innovationType, population.AssignInnovationID(), neuronType, x, y);

            if (innovationType == NEATInnovationType.NewNeuron)
            {
                newInnovation.NeuronID = AssignNeuronID();
            }

            Innovations.Add(newInnovation);

            return (this.nextNeuronID - 1); // ??????? should it be innov?
        }
        /// <summary>
        /// Create a new innovation. 
        /// </summary>
        /// <param name="input">The input neuron.</param>
        /// <param name="output">The output neuron.</param>
        /// <param name="type">The type.</param>
        public void CreateNewInnovation(long input, long output,
                 NEATInnovationType type)
        {
            NEATInnovation newInnovation = new NEATInnovation(input, output, type,
                    this.population.AssignInnovationID());

            if (type == NEATInnovationType.NewNeuron)
            {
                newInnovation.NeuronID = AssignNeuronID();
            }

            Innovations.Add(newInnovation);
        }