-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathGeneticAlgorithm.go
More file actions
190 lines (166 loc) · 5.76 KB
/
GeneticAlgorithm.go
File metadata and controls
190 lines (166 loc) · 5.76 KB
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
package ga
import (
"errors"
"fmt"
"math/rand"
"strconv"
"time"
)
func check(e error) {
if e != nil {
panic(e)
}
}
type GeneticAlgorithm struct {
Candidates Population
BestCandidate Genome
Generations int
IterationsSinceChange int
GenerateCandidate GenerateCandidateFunction
Crossover CrossoverFunction
Mutate MutateFunction
Fitness FitnessFunction
Selection SelectFunction
Output func(a ...interface{})
RulesMatch RulesMatchFunc
EncodeRules EncodeRulesFunc
DecodeRules DecodeRulesFunc
RandomEngine *rand.Rand
}
func NewGeneticAlgorithm() GeneticAlgorithm {
var geneticAlgorithm GeneticAlgorithm
geneticAlgorithm.SetGenerateCandidate(DefaultGenerateCandidate)
geneticAlgorithm.SetCrossoverFunc(DefaultCrossoverFunc)
geneticAlgorithm.SetMutateFunc(DefaultMutateFunc)
geneticAlgorithm.SetFitnessFunc(DefaultFitnessFunc)
geneticAlgorithm.SetSelectionFunc(TournamentSelection)
geneticAlgorithm.SetOutputFunc(PrintToConsole)
geneticAlgorithm.SetSeed(time.Now().Unix())
geneticAlgorithm.SetRulesMatchFunc(DefaultRulesMatchFunc)
geneticAlgorithm.SetEncodeRulesFunc(DefaultEncodeRulesFunc)
geneticAlgorithm.SetDecodeRulesFunc(DefaultDecodeRulesFunc)
return geneticAlgorithm
}
func (genA *GeneticAlgorithm) SetSeed(seed int64) {
genA.RandomEngine = rand.New(rand.NewSource(seed))
}
func (genA *GeneticAlgorithm) UpdateBestCandidate(bestGeneration Genome) {
if genA.Fitness(bestGeneration) > genA.Fitness(genA.BestCandidate) {
genA.BestCandidate = bestGeneration.Copy()
genA.IterationsSinceChange = 0
}
}
func (genA *GeneticAlgorithm) FillRandomPopulation(populationSize, candidateLength int) Population {
candidatePool := make(Population, 0)
for len(candidatePool) < populationSize {
bitstring, err := genA.GenerateCandidate(candidateLength, genA.RandomEngine)
check(err)
candidatePool = append(candidatePool, Genome{bitstring})
}
return candidatePool
}
func (genA *GeneticAlgorithm) Summarise(title string, candidatePool Population) {
output := ""
output += title
output += "{"
for _, val := range candidatePool {
output += "["
if len(val.Sequence) <= 10 {
output += val.Sequence.String()
} else {
output += fmt.Sprintf("%3v", genA.Fitness(val))
}
output += "]"
}
output += "}"
output += " Max : " + strconv.Itoa(genA.MaxFitness(candidatePool))
output += " Average : " + strconv.Itoa(genA.AverageFitness(candidatePool))
output += " Best : " + genA.MaxFitnessCandidate(candidatePool).String()
genA.Output(output)
}
func (genA *GeneticAlgorithm) Run(populationSize, bitstringLength, generations int, crossover, mutate, terminateEarly bool) error {
if genA.GenerateCandidate == nil {
return errors.New("generate func candidate is nil")
}
if genA.Crossover == nil {
return errors.New("crossover func is nil")
}
if genA.Mutate == nil {
return errors.New("mutate func is nil")
}
if genA.Fitness == nil {
return errors.New("fitness func is nil")
}
if genA.Selection == nil {
return errors.New("selection func is nil")
}
if genA.Output == nil {
return errors.New("output func is nil")
}
if genA.RandomEngine == nil {
return errors.New("random generator is not initialised")
}
if genA.RulesMatch == nil {
return errors.New("rulesMatch func is nil")
}
if genA.EncodeRules == nil {
return errors.New("encodeRules func is nil")
}
if genA.DecodeRules == nil {
return errors.New("decodeRules func is nil")
}
// Init
genA.Candidates = make(Population, 0)
genA.Candidates = genA.FillRandomPopulation(populationSize, bitstringLength)
genA.UpdateBestCandidate(genA.MaxFitnessCandidate(genA.Candidates))
// Run breeding cycles
for y := 1; y <= generations; y++ {
var bestCandidateOfGeneration Genome
bestCandidateOfGeneration = genA.MaxFitnessCandidate(genA.Candidates)
genA.UpdateBestCandidate(bestCandidateOfGeneration)
genA.Output("Iteration", y)
genA.Summarise("Start Population :", genA.Candidates)
// Tournament
breedingGround := make(Population, 0)
breedingGround = append(breedingGround, genA.Selection(genA.Fitness, genA.Candidates, genA.RandomEngine)...)
bestCandidateOfGeneration = genA.MaxFitnessCandidate(genA.Candidates)
genA.UpdateBestCandidate(bestCandidateOfGeneration)
genA.Summarise("Tournament Offspring :", breedingGround)
// Crossover
if crossover {
crossoverBreedingGround := make(Population, 0)
for i := 0; i+1 < len(breedingGround); i += 2 {
newOffspring, err := genA.Crossover(breedingGround[i], breedingGround[i+1], genA.RandomEngine)
check(err)
crossoverBreedingGround = append(crossoverBreedingGround, newOffspring...)
}
breedingGround = crossoverBreedingGround
bestCandidateOfGeneration = genA.MaxFitnessCandidate(genA.Candidates)
genA.UpdateBestCandidate(bestCandidateOfGeneration)
genA.Summarise("Crossover Offspring :", breedingGround)
}
// Mutation
if mutate {
for index := range breedingGround {
breedingGround[index] = genA.Mutate(breedingGround[index], genA.RandomEngine)
}
bestCandidateOfGeneration = genA.MaxFitnessCandidate(genA.Candidates)
genA.UpdateBestCandidate(bestCandidateOfGeneration)
genA.Summarise("Mutation Offspring :", breedingGround)
}
genA.Generations++
genA.IterationsSinceChange++
genA.Candidates = make(Population, populationSize)
copy(genA.Candidates, breedingGround)
genA.Summarise("Final Population :", breedingGround)
genA.Output()
genA.Output()
if terminateEarly && float32(genA.IterationsSinceChange) > float32(generations)*0.25 {
genA.Output("Termination : Stagnating change")
genA.Output("Best Candidate Found:", genA.BestCandidate.Sequence, "Fitness:", genA.Fitness(genA.BestCandidate))
break
}
}
genA.Output("Best Candidate Found:", genA.BestCandidate.Sequence, "Fitness:", genA.Fitness(genA.BestCandidate))
return nil
}