This repository has been archived by the owner on Aug 3, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
individuals.go
148 lines (131 loc) · 4.08 KB
/
individuals.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
140
141
142
143
144
145
146
147
148
package gago
import (
"math"
"math/rand"
"sort"
"strings"
"golang.org/x/sync/errgroup"
)
// 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, newGenome NewGenome, rng *rand.Rand) Individuals {
var indis = make(Individuals, n)
for i := range indis {
indis[i] = NewIndividual(newGenome(rng), rng)
}
return indis
}
// Apply a function to a slice of Individuals.
func (indis Individuals) Apply(f func(indi *Individual) error, parallel bool) error {
if parallel {
var g errgroup.Group
for i := range indis {
i := i // https://golang.org/doc/faq#closures_and_goroutines
g.Go(func() error {
return f(&indis[i])
})
}
return g.Wait()
}
var err error
for i := range indis {
err = f(&indis[i])
if err != nil {
return err
}
}
return err
}
// Evaluate each Individual. If parallel is true then each Individual will be
// evaluated in parallel thanks to the golang.org/x/sync/errgroup package. If
// not then a simple sequential loop will be used. Evaluating in parallel is
// only recommended for cases where evaluating an Individual takes a "long"
// time. Indeed there won't necessarily be a speed-up when evaluating in
// parallel. In fact performance can be degraded if evaluating an Individual is
// too cheap.
func (indis Individuals) Evaluate(parallel bool) {
indis.Apply(
func(indi *Individual) error { indi.Evaluate(); return nil },
parallel,
)
}
// 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()))
}