/// <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 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)); }