Beispiel #1
0
// 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
}
Beispiel #2
0
// 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
}
Beispiel #3
0
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
}