Permalink
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 10, 2018
Feb 6, 2018
Feb 10, 2018
Feb 6, 2018
Feb 10, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018
Feb 6, 2018

Newer

100644
253 lines (225 sloc)
8.4 KB

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

"""

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

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

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

51

end

52

return population

53

54

end

55

56

function score_sort_population(population::Array{Array{Float64}}, fitness_function::Function)

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

"""

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)

69

end

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

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

156

# First initialize the population

159

# Initialize iteration counter, run counter, and array to hold generations of best population

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()

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

246

end

247

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)

250

end

252

bounds = [(-2.0,2.0), (-2.0,2.0)]