// Assigns the genomes to a species. Returns new collection of species. // // Throughout evolution, NEAT maintains a list of species numbered in the order they ap- peared. In // the first generation, since there are no preexisting species, NEAT begins by creating species 1 // and placing the first genome into that species. All other genomes are placed into species as // follows: A random member of each existing species is chosen as its permanent representative. // Genomes are tested one at a time; if a genome’s distance to the representative of any existing // species is less than δt, a compatibility threshold, it is placed into this species. Otherwise, // if it is not compatible with any existing species, a new species is created and given a new // number. After the first generation, genomes are first compared with species from the previous // generation so that the same species numbers can be used to identify species throughout the run. // (Stanley, 39) // // TODO: Pick a random reprentative each time instead of permanently recording it with the species // record func (s Classic) Speciate(curr []neat.Species, genomes []neat.Genome) (next []neat.Species, err error) { // Copy the species to the new set next = make([]neat.Species, len(curr)) for i, s := range curr { next[i] = s next[i].Age = s.Age + 1 } // Iterate the genomes, looking for target species // TODO: This could be made concurrent if it is slow var δ float64 cnts := make([]int, len(curr)) for i, genome := range genomes { found := false for j, species := range next { δ, err = s.ctx.Comparer().Compare(genome, species.Example) if err != nil { return } if δ < s.CompatibilityThreshold() { genomes[i].SpeciesIdx = j cnts[j] += 1 found = true break } } if !found { genomes[i].SpeciesIdx = len(next) cnts = append(cnts, 1) species := neat.Species{ Example: neat.CopyGenome(genomes[i]), } next = append(next, species) } } // Purge unused species i := 0 for i < len(next) { if cnts[i] == 0 { next = append(next[:i], next[i+1:]...) cnts = append(cnts[:i], cnts[i+1:]...) for j := 0; j < len(genomes); j++ { if genomes[j].SpeciesIdx > i { genomes[j].SpeciesIdx -= 1 } } } else { i += 1 } } return }
// Creates the pool of potential parents, grouped by species' index. The list of genomes is also // sorted by fitness in decending order for future operations func createPool(curr neat.Population) (pool map[int]Improvements) { pool = make(map[int]Improvements, len(curr.Species)) for _, genome := range curr.Genomes { pool[genome.SpeciesIdx] = append(pool[genome.SpeciesIdx], neat.CopyGenome(genome)) } for idx, list := range pool { sort.Sort(sort.Reverse(list)) pool[idx] = list } return }
func createOffspring(ctx neat.Context, cfg ClassicSettings, cross bool, rng *rand.Rand, pool map[int]Improvements, cnts map[int]int, next *neat.Population) (err error) { var child neat.Genome for idx, cnt := range cnts { l := pool[idx] for i := 0; i < cnt; i++ { p1, p2 := pickParents(cfg, cross, rng, l, pool) if p1.ID == p2.ID { child = neat.CopyGenome(p1) } else { child, err = ctx.Crosser().Cross(p1, p2) if err != nil { return } } child.ID = ctx.NextID() child.Birth = next.Generation err = ctx.Mutator().Mutate(&child) next.Genomes = append(next.Genomes, child) } } return }