Skip to content
Permalink
Newer
Older
100644 253 lines (225 sloc) 8.4 KB
Stephen
Feb 6, 2018
1
using StatsBase
2
using PyPlot
Stephen
Feb 6, 2018
3
#=
4
Parents <— {randomly generated population}
5
While not (termination criterion)
6
Calculate the fitness of each parent in the population
7
Children <- 0
8
While | Children | < | Parents |
9
Use fitnesses to probabilistically select a pair of parents for mating
10
Mate the parents to create children c\ and c<i
11
Children <— Children U {ci,C2}
12
Loop
13
Randomly mutate some of the children
14
Parents <— Children
15
Next generation
16
17
Use continuous GA, not binary
18
19
include array of independent variables along with associated range
20
21
=#
22
23
function initialize_population(populationSize::Int, chromosomeSize::Int, bounds::Array{Tuple{Float64, Float64},1})
24
"""
Stephen
Feb 6, 2018
25
Initializes the population based on the given population size and chromosome size
Stephen
Feb 6, 2018
26
Values will be from lb to ub (lower to upper bound)
27
"""
28
29
# Check to see if we dont have enough bounds or have too many
30
if length(bounds) != chromosomeSize
31
println("Length of bounds ($(length(bounds))) not equal to specified chromosome size ($chromosomeSize)")
32
exit()
33
end
34
35
# Init population
Stephen
Feb 6, 2018
36
population = Array{Array{Float64}}(0)
Stephen
Feb 6, 2018
37
38
# Foreach member in population
39
for i = 1:populationSize
40
# Init the member
41
row = []
42
# Create member elements based on bounds
43
for bound in bounds
44
push!(row, rand(bound[1]:0.01:bound[2]))
Stephen
Feb 6, 2018
45
end
46
# Add another element to the end to keep score
47
push!(row, 1.0)
48
49
# hcat row, append to 2D population matrix
Stephen
Feb 6, 2018
50
population = push!(population, row)
Stephen
Feb 6, 2018
51
end
52
return population
53
54
end
55
Stephen
Feb 6, 2018
56
function score_sort_population(population::Array{Array{Float64}}, fitness_function::Function)
Stephen
Feb 6, 2018
57
"""
58
Args
59
population: The population to score. Must be 2D array of Float64 elements
60
fitness_function: Function to score the population
61
Must be able to take array as argument
62
For example f(x,y) should be f(A) where A[1]=x, A[2]=y
63
Returns scored population. Score is the last element of each chromosome (row)
64
in population
65
"""
Stephen
Feb 6, 2018
66
# Enumerate through population, set last element of each chromosome to score
67
for (index,member) in enumerate(population)
68
population[index][end] = fitness_function(member)
Stephen
Feb 6, 2018
69
end
Stephen
Feb 6, 2018
70
sort!(population, by = x -> x[end])
71
return population
72
end
73
74
function create_next_generation(population::Array{Array{Float64}}, recombRate::Float64)
75
"""
76
Creates children given a population and recombination rate
77
Uses an elite selection of 0.10 (rounded up)
78
Creates random chance
79
TODO: Implement ranking
80
"""
81
82
popSize = length(population)
83
memberSize = length(population[1])
84
# Init children array
85
children = Array{Array{Float64}}(0)
86
# Elite selection
87
for i in 1:Int(ceil(popSize*0.10))
88
push!(children, population[i])
89
end
90
91
# Generate rest of population
92
while length(children) < popSize
93
# Two random children
94
choices = sample(1:popSize, 2, replace=false)
95
# Check for crossover
96
if rand() < recombRate
97
# Choose xover point
98
# Use -2 since last one is score, and last actual element cant be crossedover since that means there is none
99
xover = rand(1:memberSize-2)
100
child1 = cat(1,population[choices[1]][1:xover],population[choices[2]][xover+1:end])
101
child2 = cat(1,population[choices[2]][1:xover],population[choices[1]][xover+1:end])
102
else
103
# Else no crossover, just copy
104
child1 = population[choices[1]]
105
child2 = population[choices[2]]
106
end
107
# Push child 1 and 2 to children array
108
push!(children, child1, child2)
109
end
110
111
# We might have one extra due to rounding in the elite selection, let's check
112
if length(children) != popSize
113
pop!(children)
114
end
115
return children
116
end
117
118
function mutate(population::Array{Array{Float64}}, mutateRate::Float64, bounds::Array{Tuple{Float64, Float64},1})
119
"""
120
Iterates through each chromosome and mutates if needed
121
"""
122
123
for member in population
124
for i = 1:length(member)-1
125
if rand() < mutateRate
126
member[i] = rand(bounds[i][1]:0.01:bounds[i][2])
Stephen
Feb 6, 2018
127
end
128
end
129
end
130
Stephen
Feb 6, 2018
131
return population
132
end
133
134
function GA(populationSize::Int, chromosomeSize::Int, fitness_function::Function, bounds::Array{Tuple{Float64, Float64},1})
135
"""
136
Args
137
populationSize: Total number of chromosomes
138
chromosomeSize: How long each chromosome should be (variables in fitness function)
139
fitness_function: Function to determine fitness of each solution
140
Should take an array as an arg
141
Example: f(x,y,z) = x+y+z
142
should be f(X) = X[1]+X[2]+X[3]
143
in order to allow GA to take in any function with any
144
amount of args to the fitness function
145
bounds: 1D array of tuples, each tuple is the (lower, upper) bounds
146
for each variable. Both lower and upper need to be Float64 types
147
Length should match chromosomeSize
148
e.g: [(1.0,2.0), (3.5,4.5)]
149
"""
150
151
# Set recombination and mutation rate, lower and upper bound
152
recombRate = 0.7
153
mutateRate = 0.05
154
maxIterations = 100
155
runs = 20
Stephen
Feb 6, 2018
156
# First initialize the population
Stephen
Feb 6, 2018
157
158
# Then loop for the required iterations
159
# Initialize iteration counter, run counter, and array to hold generations of best population
Stephen
Feb 6, 2018
160
i = 0
161
run = 0
162
bestGenerations = [[[0.0,0.0,Inf]]]
163
generations = Array{Array{Array{Float64}}}(0)
164
165
# For each run
166
while run < runs
167
nextPopulation = initialize_population(populationSize, chromosomeSize, bounds)
168
# Iterate through max amount of generations
169
while i < maxIterations
170
# Score and sort the population
171
population = score_sort_population(nextPopulation, fitness_function)
172
# Push current population to generations array
173
push!(generations, deepcopy(population))
174
# Create children
175
nextPopulation = create_next_generation(population, recombRate)
176
# Mutate children
177
nextPopulation = mutate(nextPopulation, mutateRate, bounds)
178
i += 1
179
end
180
#=
181
At the end of the run, if the last generations best solution
182
# is better than that of the best of all runs so far
183
set new best run so we can graph later
184
=#
185
if generations[end][1][end] < bestGenerations[end][1][end]
186
bestGenerations = deepcopy(generations)
187
end
188
clear!(:generations)
189
clear!(:population)
190
clear!(:children)
191
run += 1
192
end
193
194
# Reporting and plotting
195
# Report best solution and value
196
bestVariables = bestGenerations[end][1][1:end-1]
197
bestScore = bestGenerations[end][1][end]
198
println("Best solution\nVariables: $bestVariables\nScore: $bestScore")
199
200
# Generate best fitness vs. # generation
201
# Init score array
202
y = Array{Float64}(0)
203
# Get scores, push to array
204
for g in bestGenerations
205
push!(y,g[1][end])
206
end
207
# Plot and label
208
PyPlot.plot(y)
209
xlabel("Generation #")
210
ylabel("Fitness")
211
# Save and close
212
PyPlot.savefig("BestFitness.png")
213
PyPlot.close()
214
215
# Contour
216
# Check to make sure we only have two variables, otherwise exit
217
if length(bestGenerations[1][1]) > 3
218
println("Plotting the 4th dimension currently disabled to avoid tearing the space-time continuum")
219
quit()
Stephen
Feb 6, 2018
220
end
221
222
# Init x and y z for contour plots
223
x = y = z = Array{Float64}(0)
224
for (index, gen) in enumerate(bestGenerations)
225
if (index == 1) || (mod(index,10) == 0)
226
for m in gen
227
push!(x,m[1])
228
push!(y,m[2])
229
end
230
size = length(x)
231
z = rand(size,size)
232
for i in 1:size
233
for j in 1:size
234
z[i,j] = fitness_function([x[i],y[j]])
235
end
236
end
237
contour(x,y,z)
238
savefig("contour_gen$index.png")
239
close()
240
clear!(:x)
241
clear!(:y)
242
clear!(:z)
243
end
244
end
245
Stephen
Feb 6, 2018
246
end
247
248
function fitness_function(X)
Stephen
Feb 6, 2018
249
return e - 20*exp(-0.2*sqrt((X[1]^2 + X[2]^2)/2)) - exp((cos(2*pi*X[1]) + cos(2*pi*X[2]))/2)
Stephen
Feb 6, 2018
250
end
Stephen
Feb 6, 2018
251
Stephen
Feb 6, 2018
252
bounds = [(-2.0,2.0), (-2.0,2.0)]
253
GA(25,2,fitness_function,bounds)
You can’t perform that action at this time.