Skip to content

Commit dc29516

Browse files
committed
Implement the stochastic roulette selector
1 parent 3856972 commit dc29516

File tree

2 files changed

+62
-7
lines changed

2 files changed

+62
-7
lines changed

selection.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,42 @@ func (sel SelRoulette) Apply(n uint, indis Individuals, rng *rand.Rand) (Individ
117117
func (sel SelRoulette) Validate() error {
118118
return nil
119119
}
120+
121+
// SelStochRoulette performs a stochastic variant of the roulette wheel with
122+
// faster O(1) time complexity, at the expense of some noise.
123+
// https://arxiv.org/abs/1109.3627
124+
// https://en.wikipedia.org/wiki/Fitness_proportionate_selection @ "stochastic acceptance"
125+
type SelStochRoulette struct{}
126+
127+
// Apply SelStochRoulette
128+
func (sel SelStochRoulette) Apply(n uint, indis Individuals, rng *rand.Rand) (Individuals, []int, error) {
129+
var (
130+
selected = make(Individuals, n)
131+
indexes = make([]int, n)
132+
bestFitness = indis.FitMin()
133+
)
134+
135+
for i := range selected {
136+
var (
137+
index int
138+
accepted bool
139+
)
140+
for !accepted {
141+
index = rng.Intn(len(indis))
142+
if rng.Float64() < indis[index].Fitness/bestFitness {
143+
accepted = true
144+
}
145+
}
146+
147+
var winner = indis[index]
148+
149+
indexes[i] = index
150+
selected[i] = winner
151+
}
152+
return selected.Clone(rng), indexes, nil
153+
}
154+
155+
// Validate SelStochRoulette fields.
156+
func (sel SelStochRoulette) Validate() error {
157+
return nil
158+
}

selection_test.go

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ var (
1010
SelElitism{},
1111
SelTournament{3},
1212
SelRoulette{},
13+
SelStochRoulette{},
1314
}
1415
invalidSelectors = []Selector{
1516
SelTournament{0},
@@ -39,23 +40,23 @@ func TestSelectionSize(t *testing.T) {
3940

4041
func TestSelectionUniqueness(t *testing.T) {
4142
var (
42-
rng = newRand()
43-
indis = newIndividuals(3, NewVector, rng)
43+
rng = newRand()
44+
indis = newIndividuals(3, NewVector, rng)
4445
selectors = []Selector{
4546
SelTournament{
4647
NContestants: 2,
4748
},
4849
SelElitism{},
4950
}
5051
numIterations = 500
51-
anyNotUnique = false
52+
anyNotUnique = false
5253
)
5354
for _, selector := range selectors {
5455
for i := 0; i < numIterations; i++ {
55-
var selected, _, _= selector.Apply(2, indis, rng)
56-
var unique= false
57-
var first= selected[0].Genome.(Vector)
58-
var second= selected[1].Genome.(Vector)
56+
var selected, _, _ = selector.Apply(2, indis, rng)
57+
var unique = false
58+
var first = selected[0].Genome.(Vector)
59+
var second = selected[1].Genome.(Vector)
5960
for index := range first {
6061
if first[index] != second[index] {
6162
unique = true
@@ -136,6 +137,21 @@ func TestSelRoulette(t *testing.T) {
136137
}
137138
}
138139

140+
func TestSelStochRoulette(t *testing.T) {
141+
var (
142+
rng = newRand()
143+
indis = newIndividuals(30, NewVector, rng)
144+
sel = SelStochRoulette{}
145+
)
146+
indis.Evaluate(false)
147+
for _, n := range []uint{0, 1, 10, 30} {
148+
var selected, _, _ = sel.Apply(n, indis, rng)
149+
if len(selected) != int(n) {
150+
t.Error("SelStochRoulette didn't select the right number of individuals")
151+
}
152+
}
153+
}
154+
139155
// TestSelectorsValidate checks that each selector's Validate method doesn't
140156
// return an error in case of a valid model and that it does for invalid models.
141157
func TestSelectorsValidate(t *testing.T) {

0 commit comments

Comments
 (0)