/// <summary> /// Speciates the genomes in genomeList into the provided species. It is assumed that /// the genomeList represents all of the required genomes and that the species are currently empty. /// /// This method can be used for initialization or completely respeciating an existing genome population. /// </summary> public void SpeciateGenomes(IList <TGenome> genomeList, IList <Specie <TGenome> > specieList) { Debug.Assert(SpeciationUtils.TestEmptySpecies(specieList), "SpeciateGenomes(IList<TGenome>,IList<Species<TGenome>>) called with non-empty species"); Debug.Assert(genomeList.Count >= specieList.Count, string.Format("SpeciateGenomes(IList<TGenome>,IList<Species<TGenome>>). Specie count [{0}] is greater than genome count [{1}].", specieList.Count, genomeList.Count)); // Make a copy of genomeList and shuffle the items. List <TGenome> gList = new List <TGenome>(genomeList); Utilities.Shuffle(gList, _rng); // We evenly distribute genomes between species. // Calc how many genomes per specie. Baseline number given by integer division rounding down (by truncating fractional part). // This is guaranteed to be at least 1 because genomeCount >= specieCount. int genomeCount = genomeList.Count; int specieCount = specieList.Count; int genomesPerSpecie = genomeCount / specieCount; // Allocate genomes to species. int genomeIdx = 0; for (int i = 0; i < specieCount; i++) { Specie <TGenome> specie = specieList[i]; for (int j = 0; j < genomesPerSpecie; j++, genomeIdx++) { specie.GenomeList.Add(gList[genomeIdx]); } } // Evenly allocate any remaining genomes. for (int i = 0; i < specieCount && genomeIdx < genomeCount; i++, genomeIdx++) { specieList[i].GenomeList.Add(gList[genomeIdx]); } }
/// <summary> /// Speciates the offspring genomes in offspringList into the provided /// specieList. In contrast to SpeciateGenomes() offspringList is taken /// to be a list of new genomes (offspring) that should be added to /// existing species. That is, the species contain genomes that are not /// in offspringList that we wish to keep; typically these would be elite /// genomes that are the parents of the offspring. /// </summary> public void SpeciateOffspring(IList <TGenome> offspringList, IList <Specie <TGenome> > specieList) { // Each specie should contain at least one genome. We need at least // one existing genome per specie to act as a specie centroid in order // to define where the specie is within the encoding space. Debug.Assert(SpeciationUtils.TestPopulatedSpecies(specieList), "SpeciateOffspring(IList<TGenome>,IList<Species<TGenome>>) called with an empty specie."); // Update the centroid of each specie. If we're adding offspring this // means that old genomes have been removed from the population and // therefore the centroids are out-of-date. foreach (Specie <TGenome> specie in specieList) { specie.Centroid = CalculateSpecieCentroid(specie); } // Allocate each offspring genome to the specie it is closest to. foreach (TGenome genome in offspringList) { Specie <TGenome> closestSpecie = FindClosestSpecie(genome, specieList); closestSpecie.GenomeList.Add(genome); genome.SpecieIdx = closestSpecie.Idx; } // Recalculate each specie's centroid now that we have additional // genomes in the specieList. foreach (Specie <TGenome> specie in specieList) { specie.Centroid = CalculateSpecieCentroid(specie); } // Accumulate *all* genomes into a flat genome list. int genomeCount = 0; foreach (Specie <TGenome> specie in specieList) { genomeCount += specie.GenomeList.Count; } List <TGenome> genomeList = new List <TGenome>(genomeCount); foreach (Specie <TGenome> specie in specieList) { genomeList.AddRange(specie.GenomeList); } // Perform the main k-means loop until convergence. SpeciateUntilConvergence(genomeList, specieList); Debug.Assert(SpeciationUtils.PerformIntegrityCheck(specieList)); }
/// <summary> /// Speciates the genomes in genomeList into the provided specieList. /// It is assumed that the genomeList represents all of the required /// genomes and that the species are currently empty. /// /// This method can be used for initialization or completely respeciating /// an existing genome population. /// </summary> public void SpeciateGenomes(IList <TGenome> genomeList, IList <Specie <TGenome> > specieList) { Debug.Assert(SpeciationUtils.TestEmptySpecies(specieList), "SpeciateGenomes(IList<TGenome>,IList<Species<TGenome>>) called with non-empty species"); Debug.Assert(genomeList.Count >= specieList.Count, string.Format("SpeciateGenomes(IList<TGenome>,IList<Species<TGenome>>). Species count [{0}] is greater than genome count [{1}].", specieList.Count, genomeList.Count)); // Randomly allocate the first k genomes to their own specie. // Because there is only one genome in these species each genome // effectively represents a specie centroid. This is necessary to // ensure we get k specieList. If we randomly assign all genomes to // species from the outset and then calculate centroids then // typically some of the species become empty. This approach ensures // that each species will have at least one genome - because that // genome is the specie centroid and therefore has distance of zero // from the centroid (itself). int specieCount = specieList.Count; for (int i = 0; i < specieCount; i++) { Specie <TGenome> specie = specieList[i]; genomeList[i].SpecieIdx = specie.Idx; specie.GenomeList.Add(genomeList[i]); // Just set the specie centroid directly. specie.Centroid = genomeList[i].Position; } // Now allocate the remaining genomes based on their distance from the centroids. int genomeCount = genomeList.Count; for (int i = specieCount; i < genomeCount; i++) { TGenome genome = genomeList[i]; Specie <TGenome> closestSpecie = FindClosestSpecie(genome, specieList); genome.SpecieIdx = closestSpecie.Idx; closestSpecie.GenomeList.Add(genome); } // Recalculate each specie's centroid. foreach (Specie <TGenome> specie in specieList) { specie.Centroid = CalculateSpecieCentroid(specie); } // Perform the main k-means loop until convergence. SpeciateUntilConvergence(genomeList, specieList); Debug.Assert(SpeciationUtils.PerformIntegrityCheck(specieList)); }
/// <summary> /// Speciates the offspring genomes in offspringList into the provided specieList. In contrast to /// SpeciateGenomes() offspringList is taken to be a list of new genomes (offspring) that should be /// added to existing species. That is, the species contain genomes that are not in offspringList /// that we wish to keep; typically these would be elite genomes that are the parents of the /// offspring. /// </summary> public void SpeciateOffspring(IList <TGenome> offspringList, IList <Specie <TGenome> > specieList) { // Each specie should contain at least one genome. We need at least one existing genome per specie to act // as a specie centroid in order to define where the specie is within the encoding space. Debug.Assert(SpeciationUtils.TestPopulatedSpecies(specieList), "SpeciateOffspring(IList<TGenome>,IList<Species<TGenome>>) called with an empty specie."); // Update the centroid of each specie. If we're adding offspring this means that old genomes // have been removed from the population and therefore the centroids are out-of-date. Parallel.ForEach(specieList, _parallelOptions, delegate(Specie <TGenome> specie) { specie.Centroid = CalculateSpecieCentroid(specie); }); // Allocate each offspring genome to the specie it is closest to. Parallel.ForEach(offspringList, _parallelOptions, delegate(TGenome genome) { Specie <TGenome> closestSpecie = FindClosestSpecie(genome, specieList); lock (closestSpecie.GenomeList) { // TODO: Consider using ConcurrentQueue here (and elsewhere in this class). closestSpecie.GenomeList.Add(genome); } genome.SpecieIdx = closestSpecie.Idx; }); // Recalculate each specie's centroid now that we have additional genomes in the specieList. Parallel.ForEach(specieList, _parallelOptions, delegate(Specie <TGenome> specie) { specie.Centroid = CalculateSpecieCentroid(specie); }); // Accumulate *all* genomes into a flat genome list. int genomeCount = 0; foreach (Specie <TGenome> specie in specieList) { genomeCount += specie.GenomeList.Count; } List <TGenome> genomeList = new List <TGenome>(genomeCount); foreach (Specie <TGenome> specie in specieList) { genomeList.AddRange(specie.GenomeList); } // Perform the main k-means loop until convergence. SpeciateUntilConvergence(genomeList, specieList); Debug.Assert(SpeciationUtils.TestPopulatedSpecies(specieList), "Speciation must allocate at least one genome to each species."); }
/// <summary> /// Speciates the offspring genomes in genomeList into the provided species. In contrast to /// SpeciateGenomes() genomeList is taken to be a list of new genomes (e.g. offspring) that should be /// added to existing species. That is, the species contain genomes that are not in genomeList /// that we wish to keep; typically these would be elite genomes that are the parents of the /// offspring. /// </summary> public void SpeciateOffspring(IList <TGenome> genomeList, IList <Specie <TGenome> > specieList) { // Each specie should contain at least one genome. We need at least one existing genome per specie to act // as a specie centroid in order to define where the specie is within the encoding space. Debug.Assert(SpeciationUtils.TestPopulatedSpecies(specieList), "SpeciateOffspring(IList<TGenome>,IList<Species<TGenome>>) called with an empty specie."); // Make a copy of genomeList and shuffle the items. List <TGenome> gList = new List <TGenome>(genomeList); SharpNeat.Utility.Utilities.Shuffle(gList, _rng); // Count how many genomes we have in total. int genomeCount = gList.Count; int totalGenomeCount = genomeCount; foreach (Specie <TGenome> specie in specieList) { totalGenomeCount += specie.GenomeList.Count; } // We attempt to evenly distribute genomes between species. // Calc how many genomes per specie. Baseline number given by integer division rounding down (by truncating fractional part). // This is guaranteed to be at least 1 because genomeCount >= specieCount. int specieCount = specieList.Count; int genomesPerSpecie = totalGenomeCount / specieCount; // Sort species, smallest first. We must make a copy of specieList to do this; Species must remain at // the correct index in the main specieList. The principle here is that we wish to ensure that genomes are // allocated to smaller species in preference to larger species, this is motivated by the desire to create // evenly sized species. List <Specie <TGenome> > sList = new List <Specie <TGenome> >(specieList); sList.Sort(delegate(Specie <TGenome> x, Specie <TGenome> y) { // We use the difference in size where we aren't expecting that diff value to overflow the range of an int. return(x.GenomeList.Count - y.GenomeList.Count); }); // Add genomes into each specie in turn until they each reach genomesPerSpecie in size. int genomeIdx = 0; for (int i = 0; i < specieCount && genomeIdx < genomeCount; i++) { Specie <TGenome> specie = sList[i]; int fillcount = genomesPerSpecie - specie.GenomeList.Count; if (fillcount <= 0) { // We may encounter species with more than genomesPerSpecie genomes. Since we have // ordered the species by size we break out of this loop and allocate the remaining // genomes randomly. break; } // Don't allocate more genomes than there are remaining in genomeList. fillcount = Math.Min(fillcount, genomeCount - genomeIdx); // Allocate memory for the genomes we are about to allocate; // This eliminates potentially having to dynamically resize the list one or more times. if (specie.GenomeList.Capacity < specie.GenomeList.Count + fillcount) { specie.GenomeList.Capacity = specie.GenomeList.Count + fillcount; } // genomeIdx test not required. Already taken into account by fillCount. for (int j = 0; j < fillcount; j++) { gList[genomeIdx].SpecieIdx = specie.Idx; specie.GenomeList.Add(gList[genomeIdx++]); } } // Evenly allocate any remaining genomes. int[] specieIdxArr = new int[specieCount]; for (int i = 0; i < specieCount; i++) { specieIdxArr[i] = i; } SharpNeat.Utility.Utilities.Shuffle(specieIdxArr, _rng); for (int i = 0; i < specieCount && genomeIdx < genomeCount; i++, genomeIdx++) { int specieIdx = specieIdxArr[i]; gList[genomeIdx].SpecieIdx = specieIdx; specieList[specieIdx].GenomeList.Add(gList[genomeIdx]); } Debug.Assert(SpeciationUtils.PerformIntegrityCheck(specieList)); }