gago/individuals.go

118 lines
3.1 KiB
Go
Raw Permalink Normal View History

2021-09-11 12:47:32 +02:00
package gago
import (
"math"
"math/rand"
"sort"
"strings"
)
// Individuals is a convenience type, methods that belong to an Individual can
// be called declaratively.
type Individuals []Individual
// String representation of a slice of Individuals.
func (indis Individuals) String() string {
var str string
for _, indi := range indis {
str += indi.String() + "\n"
}
return strings.TrimSuffix(str, "\n")
}
// Clone returns the same exact same slice of individuals but with different
// pointers and ID fields.
func (indis Individuals) Clone(rng *rand.Rand) Individuals {
var clones = make(Individuals, len(indis))
for i, indi := range indis {
clones[i] = indi.Clone(rng)
}
return clones
}
// Generate a slice of n new individuals.
func newIndividuals(n int, gf GenomeFactory, rng *rand.Rand) Individuals {
var indis = make(Individuals, n)
for i := range indis {
indis[i] = NewIndividual(gf(rng), rng)
}
return indis
}
// Evaluate each individual.
func (indis Individuals) Evaluate() {
for i := range indis {
indis[i].Evaluate()
}
}
// Mutate each individual.
func (indis Individuals) Mutate(mutRate float64, rng *rand.Rand) {
for i := range indis {
if rng.Float64() < mutRate {
indis[i].Mutate(rng)
}
}
}
// SortByFitness ascendingly sorts individuals by fitness.
func (indis Individuals) SortByFitness() {
var less = func(i, j int) bool { return indis[i].Fitness < indis[j].Fitness }
sort.Slice(indis, less)
}
// IsSortedByFitness checks if individuals are ascendingly sorted by fitness.
func (indis Individuals) IsSortedByFitness() bool {
var less = func(i, j int) bool { return indis[i].Fitness < indis[j].Fitness }
return sort.SliceIsSorted(indis, less)
}
// SortByDistanceToMedoid sorts Individuals according to their distance to the
// medoid. The medoid is the Individual that has the lowest average distance to
// the rest of the Individuals.
func (indis Individuals) SortByDistanceToMedoid(dm DistanceMemoizer) {
var (
avgDists = calcAvgDistances(indis, dm)
less = func(i, j int) bool {
return avgDists[indis[i].ID] < avgDists[indis[j].ID]
}
)
sort.Slice(indis, less)
}
// Extract the fitness of a slice of individuals into a float64 slice.
func (indis Individuals) getFitnesses() []float64 {
var fitnesses = make([]float64, len(indis))
for i, indi := range indis {
fitnesses[i] = indi.Fitness
}
return fitnesses
}
// FitMin returns the best fitness of a slice of individuals.
func (indis Individuals) FitMin() float64 {
if indis.IsSortedByFitness() {
return indis[0].Fitness
}
return minFloat64s(indis.getFitnesses())
}
// FitMax returns the best fitness of a slice of individuals.
func (indis Individuals) FitMax() float64 {
if indis.IsSortedByFitness() {
return indis[len(indis)-1].Fitness
}
return maxFloat64s(indis.getFitnesses())
}
// FitAvg returns the average fitness of a slice of individuals.
func (indis Individuals) FitAvg() float64 {
return meanFloat64s(indis.getFitnesses())
}
// FitStd returns the standard deviation of the fitness of a slice of
// individuals.
func (indis Individuals) FitStd() float64 {
return math.Sqrt(varianceFloat64s(indis.getFitnesses()))
}