/// <summary> /// Merge new genomes into an existing set of species. /// </summary> /// <param name="genomeList">A list of genomes that have not yet been assigned a species.</param> /// <param name="speciesArr">An array of pre-existing species</param> /// <param name="rng">Random source.</param> public void SpeciateAdd(IList <NeatGenome <T> > genomeList, Species <T>[] speciesArr, IRandomSource rng) { // Create a temporary working array of species modification bits. var updateBits = new bool[speciesArr.Length]; // Allocate the new genomes to the species centroid they are nearest too. Parallel.ForEach(genomeList, _parallelOptions, (genome) => { var nearestSpeciesIdx = SpeciationUtils.GetNearestSpecies(_distanceMetric, genome, speciesArr); var nearestSpecies = speciesArr[nearestSpeciesIdx]; lock (nearestSpecies.GenomeList) { nearestSpecies.GenomeList.Add(genome); } // Set the modification bit for the species. updateBits[nearestSpeciesIdx] = true; }); // Recalc the species centroids for species that have been modified. RecalcCentroids_GenomeList(speciesArr, updateBits); // Run the k-means algorithm. RunKMeans(speciesArr); }
private int KMeansIteration(Species <T>[] speciesArr, bool[] updateBits) { int reallocCount = 0; Array.Clear(updateBits, 0, updateBits.Length); // Loop species. // Note. The nested parallel loop here is intentional and should give good thread concurrency in the general case. // For more info see: "Is it OK to use nested Parallel.For loops?" // https://blogs.msdn.microsoft.com/pfxteam/2012/03/14/is-it-ok-to-use-nested-parallel-for-loops/ Parallel.For(0, speciesArr.Length, _parallelOptions, (speciesIdx) => { var species = speciesArr[speciesIdx]; // Loop genomes in the current species. Parallel.ForEach(species.GenomeById.Values, _parallelOptions, (genome) => { // Determine the species centroid the genome is nearest to. var nearestSpeciesIdx = SpeciationUtils.GetNearestSpecies(_distanceMetric, genome, speciesArr); // If the nearest species is not the species the genome is currently in then move the genome. if (nearestSpeciesIdx != speciesIdx) { // Move genome. // Note. We can't modify species.GenomeById while we are enumerating through it, therefore we record the IDs // of the genomes to be removed and remove them once we leave the enumeration loop. lock (species.PendingRemovesList) { species.PendingRemovesList.Add(genome.Id); } var nearestSpecies = speciesArr[nearestSpeciesIdx]; lock (nearestSpecies.PendingAddsList) { nearestSpecies.PendingAddsList.Add(genome); } // Set the modification bits for the two species. updateBits[speciesIdx] = true; updateBits[nearestSpeciesIdx] = true; // Track the number of re-allocations. Interlocked.Increment(ref reallocCount); } }); }); // Complete moving of genomes to their new species. Parallel.ForEach(speciesArr, _parallelOptions, (species) => species.CompletePendingMoves()); // Recalc the species centroids for species that have been modified. RecalcCentroids_GenomeById(speciesArr, updateBits); return(reallocCount); }
private void KMeansComplete(Species <T>[] speciesArr) { // Check for empty species (this can happen with k-means), and if there are any then // move genomes into those empty species. var emptySpeciesArr = speciesArr.Where(x => x.GenomeById.Count == 0).ToArray(); if (emptySpeciesArr.Length != 0) { SpeciationUtils.PopulateEmptySpecies(_distanceMetric, emptySpeciesArr, speciesArr); } // Transfer all genomes from GenomeById to GenomeList. Parallel.ForEach(speciesArr, _parallelOptions, species => species.FlushWorkingDictionary()); }
private void RecalcCentroids_GenomeList(Species <T>[] speciesArr, bool[] updateBits) { Parallel.ForEach( Enumerable.Range(0, speciesArr.Length).Where(i => updateBits[i]), _parallelOptions, () => new List <ConnectionGenes <T> >(), (speciesIdx, loopState, connGenesList) => { var species = speciesArr[speciesIdx]; SpeciationUtils.ExtractConnectionGenes(connGenesList, species.GenomeList); species.Centroid = _distanceMetric.CalculateCentroid(connGenesList); return(connGenesList); }, (connGenesList) => connGenesList.Clear()); }
private int KMeansIteration(Species <T>[] speciesArr, bool[] updateBits) { int reallocCount = 0; Array.Clear(updateBits, 0, updateBits.Length); // Loop species. for (int speciesIdx = 0; speciesIdx < speciesArr.Length; speciesIdx++) { var species = speciesArr[speciesIdx]; // Loop genomes in the current species. foreach (var genome in species.GenomeById.Values) { // Determine the species centroid the genome is nearest to. var nearestSpeciesIdx = SpeciationUtils.GetNearestSpecies(_distanceMetric, genome, speciesArr); // If the nearest species is not the species the genome is currently in then move the genome. if (nearestSpeciesIdx != speciesIdx) { // Move genome. // Note. We can't modify species.GenomeById while we are enumerating through it, therefore we record the IDs // of the genomes to be removed and remove them once we leave the enumeration loop. species.PendingRemovesList.Add(genome.Id); speciesArr[nearestSpeciesIdx].PendingAddsList.Add(genome); // Set the modification bits for the two species. updateBits[speciesIdx] = true; updateBits[nearestSpeciesIdx] = true; // Track the number of re-allocations. reallocCount++; } } } // Complete moving of genomes to their new species. foreach (var species in speciesArr) { species.CompletePendingMoves(); } // Recalc the species centroids for species that have been modified. RecalcCentroids_GenomeById(speciesArr, updateBits); return(reallocCount); }
private void RecalcCentroids_GenomeById(Species <T>[] speciesArr, bool[] updateBits) { Parallel.ForEach( Enumerable.Range(0, speciesArr.Length).Where(i => updateBits[i]), _parallelOptions, () => new List <ConnectionGenes <T> >(), (speciesIdx, loopState, connGenesList) => { var species = speciesArr[speciesIdx]; // Extract the ConnectionGenes<T> object from each genome in the GenomeById dictionary. SpeciationUtils.ExtractConnectionGenes(connGenesList, species.GenomeById); // Calculate the centroid for the extracted connection genes. species.Centroid = _distanceMetric.CalculateCentroid(connGenesList); return(connGenesList); }, (connGenesList) => connGenesList.Clear()); }
private void RecalcCentroids_GenomeList( Species <T>[] speciesArr, bool[] updateBits) { // Create a temporary, reusable, working list. var tmpConnGenes = new List <ConnectionGenes <T> >(); for (int i = 0; i < speciesArr.Length; i++) { if (updateBits[i]) { var species = speciesArr[i]; // Extract the ConnectionGenes<T> object from each genome in GenomeList. SpeciationUtils.ExtractConnectionGenes(tmpConnGenes, species.GenomeList); // Calculate the centroid for the extracted connection genes. species.Centroid = _distanceMetric.CalculateCentroid(tmpConnGenes); } } tmpConnGenes.Clear(); }