-
Notifications
You must be signed in to change notification settings - Fork 96
/
individual.go
139 lines (119 loc) · 4.08 KB
/
individual.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package gago
import (
"math"
"math/rand"
"sort"
)
// EVALUATIONS tracks the total number of times the fitness function was
// evaluated.
var EVALUATIONS = 0
// A Genome is an object that can have any number and kinds of properties. As
// long as it can be evaluated, mutated and crossedover then it can evolved.
type Genome interface {
Evaluate() float64
Mutate(rng *rand.Rand)
Crossover(genome Genome, rng *rand.Rand) (Genome, Genome)
}
// A GenomeMaker is a method that generates a new Genome with random properties.
type GenomeMaker func(rng *rand.Rand) Genome
// An Individual represents a potential solution to a problem. Each individual
// is defined by its genome, which is a slice containing genes. Every gene is a
// floating point number. The fitness is the individual's phenotype and is
// represented by a floating point number.
type Individual struct {
Genome Genome
Fitness float64
Evaluated bool
}
// MakeIndividual returns a fresh individual.
func MakeIndividual(genome Genome) Individual {
return Individual{
Genome: genome,
Fitness: math.Inf(1),
Evaluated: false,
}
}
// DeepCopy an individual.
func (indi Individual) DeepCopy() Individual {
return MakeIndividual(indi.Genome)
}
// Evaluate the fitness of an individual. Don't evaluate individuals that have
// already been evaluated.
func (indi *Individual) Evaluate() {
if indi.Evaluated == false {
indi.Fitness = indi.Genome.Evaluate()
EVALUATIONS++
indi.Evaluated = true
}
}
// Mutate an individual by calling the Mutate method of it's Genome.
func (indi *Individual) Mutate(rng *rand.Rand) {
indi.Genome.Mutate(rng)
indi.Evaluated = false
}
// Crossover an individual by calling the Crossover method of it's Genome.
func (indi *Individual) Crossover(indi2 Individual, rng *rand.Rand) (Individual, Individual) {
var (
genome1, genome2 = indi.Genome.Crossover(indi2.Genome, rng)
offspring1, offspring2 = MakeIndividual(genome1), MakeIndividual(genome2)
)
return offspring1, offspring2
}
// Individuals is a convenience type, methods that belong to an Individual can
// be called declaratively.
type Individuals []Individual
// Generate a slice of n new individuals.
func makeIndividuals(n int, gm GenomeMaker, rng *rand.Rand) Individuals {
var indis = make(Individuals, n)
for i := range indis {
indis[i] = MakeIndividual(gm(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)
}
}
}
// Sort the individuals of a population in ascending order based on their
// fitness. The convention is that we always want to minimize a function. A
// function f(x) can be function maximized by minimizing -f(x) or 1/f(x).
func (indis Individuals) Len() int { return len(indis) }
func (indis Individuals) Less(i, j int) bool { return indis[i].Fitness < indis[j].Fitness }
func (indis Individuals) Swap(i, j int) { indis[i], indis[j] = indis[j], indis[i] }
// Sort is a convenience method for calling the Sort method of the sort package.
func (indis *Individuals) Sort() { sort.Sort(indis) }
// Sample k unique individuals from a slice of n individuals.
func (indis Individuals) sample(k int, rng *rand.Rand) (sample Individuals, indexes []int) {
indexes = randomInts(k, 0, len(indis), rng)
sample = make(Individuals, k)
for i := 0; i < k; i++ {
sample[i] = indis[indexes[i]]
}
return
}
// 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
}
// FitnessMean returns the average fitness of a slice of individuals.
func (indis Individuals) FitnessMean() float64 {
return mean(indis.getFitnesses())
}
// FitnessVar returns the variance of the fitness of a slice of individuals.
func (indis Individuals) FitnessVar() float64 {
return variance(indis.getFitnesses())
}