/// <summary>
        /// Speciates the genomes in genomeList into the number of species specified by specieCount
        /// and returns a newly constructed list of Species objects containing the speciated genomes.
        /// </summary>
        public List<Species> InitializeSpeciation(List<IGenome> genomeList, int specieCount)
        {
            // Create empty specieList.
            List<Species> specieList = new List<Species>(specieCount);
            for (int i = 0; i < specieCount; i++)
            {
                Species specie = new Species();
                specie.Centroid = new SortedDictionary<double, double>();
                specie.SpeciesId = i; // JUSTIN: Is this a good idea? Or should we use a unique id for species? (nah, I think we should use the index..)
                specieList.Add(specie);
            }

            // Speciate genomes into the empty species.
            SpeciateGenomes(genomeList, specieList);
            return specieList;
        }
예제 #2
0
		private IGenome RouletteWheelSelect(Species species)
		{
			double selectValue = (Utilities.NextDouble() * species.SelectionCountTotalFitness);
			double accumulator=0.0;

			int genomeBound = species.Members.Count;
			for(int genomeIdx=0; genomeIdx<genomeBound; genomeIdx++)
			{
				IGenome genome = species.Members[genomeIdx];

				accumulator += genome.Fitness;
				if(selectValue <= accumulator)
					return genome;
			}
			// Should never reach here.
			return null;
		}
예제 #3
0
		/// <summary>
		/// Biased select.
		/// </summary>
		/// <param name="species">Species to select from.</param>
		/// <returns></returns>
		private IGenome TournamentSelect(Species species) {
			double bestFound= 0.0;
			IGenome bestGenome=null;
			int bound = species.Members.Count;
			for(int i=0;i<neatParameters.tournamentSize;i++) {
				IGenome next= species.Members[Utilities.Next(bound)];
				if (next.Fitness > bestFound) {
					bestFound=next.Fitness;
					bestGenome=next;
				}
			}
			return bestGenome;
		}
예제 #4
0
        private bool IsGenomeInSpecies(IGenome genome, Species compareWithSpecies, EvolutionAlgorithm ea)
        {
            //			// Pick a member of the species at random.
            //			IGenome compareWithGenome = compareWithSpecies.Members[(int)Math.Floor(compareWithSpecies.Members.Count * Utilities.NextDouble())];
            //			return (genome.CalculateCompatibility(compareWithGenome, ea.NeatParameters) < ea.NeatParameters.compatibilityThreshold);

            // Compare against the species champ. The species champ is the exemplar that represents the species.
            IGenome compareWithGenome = compareWithSpecies.Members[0];
            //IGenome compareWithGenome = compareWithSpecies.Members[(int)Math.Floor(compareWithSpecies.Members.Count * Utilities.NextDouble())];
            return genome.IsCompatibleWithGenome(compareWithGenome, ea.NeatParameters);
        }
예제 #5
0
        /// <summary>
        /// This version of AddGenomeToSpeciesTable is used by RedetermineSpeciation(). It allows us to
        /// pass in the genome's original species object, which we can then re-use if the genome does not
        /// match any of our existing species and needs to be placed into a new species of it's own.
        /// The old species object can be used directly because it should already have already had all of 
        /// its genome sremoved by RedetermineSpeciation() before being passed in here.
        /// </summary>
        /// <param name="ea"></param>
        /// <param name="genome"></param>
        /// <param name="originalSpecies"></param>
        private void AddGenomeToSpeciesTable(EvolutionAlgorithm ea, IGenome genome, Species originalSpecies)
        {
            Species species = DetermineSpecies(ea, genome);
            if(species==null)
            {
                // The genome is not in one of the existing (new) species. Is this genome's old
                // species already in the new table?
                species = (Species)speciesTable[genome.SpeciesId];
                if(species!=null)
                {
                    // The genomes old species is already in the table but the genome no longer fits into that
                    // species. Therefore we need to create an entirely new species.
                    species = new Species();
                    species.SpeciesId = nextSpeciesId++;
                }
                else
                {
                    // We can re-use the oldSpecies object.
                    species = originalSpecies;
                }
                speciesTable.Add(species.SpeciesId, species);
            }

            //----- The genome is a member of an existing species.
            genome.SpeciesId = species.SpeciesId;
            species.Members.Add(genome);
        }
예제 #6
0
        private void AddGenomeToSpeciesTable(EvolutionAlgorithm ea, IGenome genome)
        {
            Species species = DetermineSpecies(ea, genome);
            if(species==null)
            {
                species = new Species();

                // Completely new species. Generate a speciesID.
                species.SpeciesId = nextSpeciesId++;
                speciesTable.Add(species.SpeciesId, species);
            }

            //----- The genome is a member of an existing species.
            genome.SpeciesId = species.SpeciesId;
            species.Members.Add(genome);
        }
        /// <summary>
        /// Recalculate the specie centroid based on the genomes currently in the specie.
        /// This is the euclidean distance centroid, which is average vector across all dimensions
        /// </summary>
        private SortedDictionary<double,double> CalculateSpecieCentroid(Species specie)
        {
            specie.Centroid.Clear();

            double numInSpecies = specie.Members.Count; // we will pre-divide every weight by this factor before adding it to the centroid, so by the end of the summation we will have the average

            foreach (IGenome g in specie.Members)
            {
                foreach (ConnectionGene cg in (g as NeatGenome.NeatGenome).ConnectionGeneList)
                {
                    if (specie.Centroid.ContainsKey(cg.InnovationId))
                    {
                        specie.Centroid[cg.InnovationId] = specie.Centroid[cg.InnovationId] + (cg.Weight / numInSpecies);
                    }
                    else
                    {
                        specie.Centroid[cg.InnovationId] = (cg.Weight / numInSpecies);
                    }
                }
            }

            // The centroid calculation is a function of the distance metric.
            return specie.Centroid;
        }
        /// <summary>
        /// Perform the main k-means loop until no genome reallocations occur or some maximum number of loops
        /// has been performed. Theoretically a small number of reallocations may occur for a great many loops
        /// therefore we require the additional max loops threshold exit strategy - the clusters should be pretty
        /// stable and well defined after a few loops even if the the algorithm hasn't converged completely.
        /// </summary>
        private void SpeciateUntilConvergence(List <IGenome> genomeList, List <Species> specieList)
        {
            List <Species> emptySpecieList = new List <Species>();
            int            specieCount     = specieList.Count;

            // Array of flags that indicate if a specie was modified (had genomes allocated to and/or from it).
            bool[] specieModArr = new bool[specieCount];

            // Main k-means loop.
            for (int loops = 0; loops < __MAX_KMEANS_LOOPS; loops++)
            {
                // Track number of reallocations made on each loop.
                int reallocations = 0;

                // Loop over genomes. For each one find the specie it is closest to; if it is not the specie
                // it is currently in then reallocate it.
                foreach (IGenome genome in genomeList)
                {
                    Species closestSpecie = FindClosestSpecie(genome, specieList);
                    if (genome.SpeciesId != closestSpecie.SpeciesId)
                    {
                        // Track which species have been modified.
                        specieModArr[genome.SpeciesId]        = true;
                        specieModArr[closestSpecie.SpeciesId] = true;

                        // Add the genome to its new specie and set its speciesId accordingly.
                        // For now we leave the genome in its original species; It's more efficient to determine
                        // all reallocations and then remove reallocated genomes from their origin specie all together;
                        // This is because we can shuffle down the remaining genomes in a specie to fill the gaps made by
                        // the removed genomes - and do so in one round of shuffling instead of shuffling to fill a gap on
                        // each remove.
                        closestSpecie.Members.Add(genome);
                        genome.SpeciesId = closestSpecie.SpeciesId;
                        reallocations++;
                    }
                }

                // Complete the reallocations.
                for (int i = 0; i < specieCount; i++)
                {
                    if (!specieModArr[i])
                    {   // Specie not changed. Skip.
                        continue;
                    }

                    // Reset flag.
                    specieModArr[i] = false;

                    // Remove the genomes that have been allocated to other other species. We fill the resulting
                    // gaps by shuffling down the remaining genomes.
                    Species specie = specieList[i];
                    specie.Members.RemoveAll(delegate(IGenome genome)
                    {
                        return(genome.SpeciesId != specie.SpeciesId);
                    });

                    // Track empty species. We will allocate genomes to them after this loop.
                    // This is necessary as some distance metrics can result in empty species occuring.
                    if (0 == specie.Members.Count)
                    {
                        emptySpecieList.Add(specie);
                    }
                    else
                    {
                        // Recalc the specie centroid now that it contains a different set of genomes.
                        specie.Centroid = CalculateSpecieCentroid(specie);
                    }
                }

                // Check for empty species. We need to reallocate some genomes into the empty specieList to maintain the
                // required number of species.
                if (0 != emptySpecieList.Count)
                {
                    // We find the genomes in the population as a whole that are furthest from their containing specie's
                    // centroid genome - we call these outlier genomes. We then move these genomes into the empty species to
                    // act as the sole member and centroid of those speciea; These act as specie seeds for the next k-means loop.
                    IGenome[] genomeByDistanceArr = GetGenomesByDistanceFromSpecie(genomeList, specieList);

                    // Reallocate each of the outlier genomes from their current specie to an empty specie.
                    int emptySpecieCount = emptySpecieList.Count;
                    int outlierIdx       = 0;
                    for (int i = 0; i < emptySpecieCount; i++)
                    {
                        // Find the next outlier genome that can be re-allocated. Skip genomes that are the
                        // only member of a specie - that would just create another empty specie.
                        IGenome genome;
                        Species sourceSpecie;
                        do
                        {
                            genome       = genomeByDistanceArr[outlierIdx++];
                            sourceSpecie = specieList[genome.SpeciesId];
                        }while (sourceSpecie.Members.Count == 1 && outlierIdx < genomeByDistanceArr.Length);

                        if (outlierIdx == genomeByDistanceArr.Length)
                        {   // Theoretically impossible. We do the test so that we get an easy to trace error message if it does happen.
                            throw new Exception("Error finding outlier genome. No outliers could be found in any specie with more than 1 genome.");
                        }

                        // Get ref to the empty specie and register both source and target specie with specieModArr.
                        Species emptySpecie = emptySpecieList[i];
                        specieModArr[emptySpecie.SpeciesId]  = true;
                        specieModArr[sourceSpecie.SpeciesId] = true;

                        // Reallocate the genome. Here we do the remove operation right away; We aren't expecting to deal with many empty
                        // species, usually it will be one or two at most; Any more and there's probably something wrong with the distance
                        // metric, e.g. maybe it doesn't satisfy the triangle inequality (see wikipedia).
                        // Another reason to remove right is to eliminate the possibility of removing multiple outlier genomes from the
                        // same specie and potentially leaving it empty; The test in the do-while loop above only takes genomes from
                        // currently non-empty species.
                        sourceSpecie.Members.Remove(genome);
                        emptySpecie.Members.Add(genome);
                        genome.SpeciesId = emptySpecie.SpeciesId;
                        reallocations++;
                    }

                    // Recalculate centroid for all affected species.
                    for (int i = 0; i < specieCount; i++)
                    {
                        if (specieModArr[i])
                        {   // Reset flag while we're here. Do this first to help maintain CPU cache coherency (we just tested it).
                            specieModArr[i]        = false;
                            specieList[i].Centroid = CalculateSpecieCentroid(specieList[i]);
                        }
                    }

                    // Clear emptySpecieList after using it. Otherwise we are holding old references and thus creating
                    // work for the garbage collector.
                    emptySpecieList.Clear();
                }

                // Exit the loop if no genome reallocations have occured. The species are stable, speciation is completed.
                if (0 == reallocations)
                {
                    break;
                }
            }
        }