forked from MaxHalford/eaopt
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindividuals.go
144 lines (125 loc) · 3.65 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
package eaopt
import (
"math"
"math/rand"
"runtime"
"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 uint, newGenome func(rng *rand.Rand) Genome, rng *rand.Rand) Individuals {
var indis = make(Individuals, n)
for i := range indis {
indis[i] = NewIndividual(newGenome(rng), rng)
}
return indis
}
// Evaluate each Individual in a slice.
func (indis Individuals) Evaluate(parallel bool) error {
if !parallel {
var err error
for i := range indis {
err = indis[i].Evaluate()
if err != nil {
return err
}
}
return nil
}
var (
nWorkers = runtime.GOMAXPROCS(-1)
n = len(indis)
chunkSize = (n + nWorkers - 1) / nWorkers
g errgroup.Group
)
for a := 0; a < n; a += chunkSize {
a := a // https://golang.org/doc/faq#closures_and_goroutines
var b = minInt(a+chunkSize, n)
g.Go(func() error {
return indis[a:b].Evaluate(false)
})
}
return g.Wait()
}
// 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 worst 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()))
}