-
Notifications
You must be signed in to change notification settings - Fork 96
/
selection.go
122 lines (110 loc) · 3.38 KB
/
selection.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
package eaopt
import (
"errors"
"fmt"
"math/rand"
"sort"
)
// Selector chooses a subset of size n from a group of individuals. The group of
// individuals a Selector is applied to is expected to be sorted.
type Selector interface {
Apply(n uint, indis Individuals, rng *rand.Rand) (selected Individuals, indexes []int, err error)
Validate() error
}
// SelElitism selection returns the n best individuals of a group.
type SelElitism struct{}
// Apply SelElitism.
func (sel SelElitism) Apply(n uint, indis Individuals, rng *rand.Rand) (Individuals, []int, error) {
indis.SortByFitness()
return indis[:n].Clone(rng), newInts(n), nil
}
// Validate SelElitism fields.
func (sel SelElitism) Validate() error {
return nil
}
// SelTournament samples individuals through tournament selection. The
// tournament is composed of randomly chosen individuals. The winner of the
// tournament is the chosen individual with the lowest fitness. The obtained
// individuals are all distinct, in other words there are no repetitions.
type SelTournament struct {
NContestants uint
}
// Apply SelTournament.
func (sel SelTournament) Apply(n uint, indis Individuals, rng *rand.Rand) (Individuals, []int, error) {
// Check that the number of individuals is large enough
if uint(len(indis))-n < sel.NContestants-1 || len(indis) < int(n) {
return nil, nil, fmt.Errorf("not enough individuals to select %d "+
"with NContestants = %d, have %d individuals and need at least %d",
n, sel.NContestants, len(indis), sel.NContestants+n-1)
}
var (
winners = make(Individuals, n)
indexes = make([]int, n)
notSelectedIdxs = newInts(uint(len(indis)))
)
for i := range winners {
// Sample contestants
var (
contestants, idxs, _ = sampleInts(notSelectedIdxs, sel.NContestants, rng)
winnerIdx int
)
// Find the best contestant
winners[i] = indis[contestants[0]]
err := winners[i].Evaluate()
if err != nil {
return nil, nil, err
}
winnerIdx = idxs[0]
for j, idx := range contestants {
if indis[idx].GetFitness() < winners[i].Fitness {
winners[i] = indis[idx]
indexes[i] = idx
winnerIdx = idxs[j]
}
}
// Ban the winner from re-participating
notSelectedIdxs = append(notSelectedIdxs[:winnerIdx], notSelectedIdxs[winnerIdx+1:]...)
}
return winners.Clone(rng), indexes, nil
}
// Validate SelTournament fields.
func (sel SelTournament) Validate() error {
if sel.NContestants < 1 {
return errors.New("NContestants should be higher than 0")
}
return nil
}
// SelRoulette samples individuals through roulette wheel selection (also known
// as fitness proportionate selection).
type SelRoulette struct{}
func buildWheel(fitnesses []float64) []float64 {
var (
n = len(fitnesses)
wheel = make([]float64, n)
)
for i, v := range fitnesses {
wheel[i] = fitnesses[n-1] - v + 1
}
return cumsum(divide(wheel, sumFloat64s(wheel)))
}
// Apply SelRoulette.
func (sel SelRoulette) Apply(n uint, indis Individuals, rng *rand.Rand) (Individuals, []int, error) {
var (
selected = make(Individuals, n)
indexes = make([]int, n)
wheel = buildWheel(indis.getFitnesses())
)
for i := range selected {
var (
index = sort.SearchFloat64s(wheel, rng.Float64())
winner = indis[index]
)
indexes[i] = index
selected[i] = winner
}
return selected.Clone(rng), indexes, nil
}
// Validate SelRoulette fields.
func (sel SelRoulette) Validate() error {
return nil
}