Skip to content
Permalink
Newer
Older
100644 262 lines (233 sloc) 8.8 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
run += 1
191
end
192
193
# Reporting and plotting
194
# Report best solution and value
195
bestVariables = bestGenerations[end][1][1:end-1]
196
bestScore = bestGenerations[end][1][end]
197
println("Best solution\nVariables: $bestVariables\nScore: $bestScore")
198
199
# Generate best fitness vs. # generation
200
# Init score array
201
y = Array{Float64}(0)
202
# Get scores, push to array
203
for g in bestGenerations
204
push!(y,g[1][end])
205
end
206
# Plot and label
207
PyPlot.plot(y)
208
xlabel("Generation #")
209
ylabel("Fitness")
210
# Save and close
211
PyPlot.savefig("BestFitness.png")
212
PyPlot.close()
213
214
# Contour
215
# Check to make sure we only have two variables, otherwise exit
216
if length(bestGenerations[1][1]) > 3
217
println("Plotting the 4th dimension currently disabled to avoid tearing the space-time continuum")
218
quit()
Stephen
Feb 6, 2018
219
end
221
222
# Pick X and Y range for contour plot based on bounds
223
xrange = bounds[1][1]:0.01:bounds[1][2]
224
yrange = bounds[2][1]:0.01:bounds[2][2]
225
size = length(xrange)
226
# Init z array for values of fitness function
227
z = zeros(size,size)
228
for i in 1:size
229
for j in 1:size
230
# Generate a z value for each x and y value
231
z[i,j] = fitness_function([xrange[i],yrange[j]])
232
end
233
end
234
# If we are at 1st gen or evry 10th, plot
235
for (index, gen) in enumerate(bestGenerations)
236
if (index == 1) || (mod(index,10) == 0)
237
# Re-init x and y for the generation
238
x = Array{Float64}(0)
239
y = Array{Float64}(0)
240
# Push x and y to separate arrays
241
for m in gen
242
push!(x,m[1])
243
push!(y,m[2])
244
end
245
# Plt the contour plot
246
contour(xrange,yrange,z)
247
# Then plot the scatter underneath
248
scatter(x,y)
249
title("Generation $index")
250
savefig("contour_gen$index.png")
251
close()
252
end
253
end
254
Stephen
Feb 6, 2018
255
end
256
257
function fitness_function(X)
Stephen
Feb 6, 2018
258
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
259
end
Stephen
Feb 6, 2018
260
Stephen
Feb 6, 2018
261
bounds = [(-2.0,2.0), (-2.0,2.0)]
262
GA(25,2,fitness_function,bounds)
You can’t perform that action at this time.