using StatsBase
#=
Parents <— {randomly generated population}
While not (termination criterion)
Calculate the fitness of each parent in the population
Children <- 0
While | Children | < | Parents |
Use fitnesses to probabilistically select a pair of parents for mating
Mate the parents to create children c\ and c* x[end])
return population
end
function create_next_generation(population::Array{Array{Float64}}, recombRate::Float64)
"""
Creates children given a population and recombination rate
Uses an elite selection of 0.10 (rounded up)
Creates random chance
TODO: Implement ranking
"""
popSize = length(population)
memberSize = length(population[1])
# Init children array
children = Array{Array{Float64}}(0)
# Elite selection
for i in 1:Int(ceil(popSize*0.10))
push!(children, population[i])
end
# Generate rest of population
while length(children) < popSize
# Two random children
choices = sample(1:popSize, 2, replace=false)
# Check for crossover
if rand() < recombRate
# Choose xover point
# Use -2 since last one is score, and last actual element cant be crossedover since that means there is none
xover = rand(1:memberSize-2)
child1 = cat(1,population[choices[1]][1:xover],population[choices[2]][xover+1:end])
child2 = cat(1,population[choices[2]][1:xover],population[choices[1]][xover+1:end])
else
# Else no crossover, just copy
child1 = population[choices[1]]
child2 = population[choices[2]]
end
# Push child 1 and 2 to children array
push!(children, child1, child2)
end
# We might have one extra due to rounding in the elite selection, let's check
if length(children) != popSize
pop!(children)
end
return children
end
function mutate(population::Array{Array{Float64}}, mutateRate::Float64, bounds::Array{Tuple{Float64, Float64},1})
"""
Iterates through each chromosome and mutates if needed
"""
for member in population
for i = 1:length(member)-1
if rand() < mutateRate
member[i] = rand(bounds[i][1]:0.10:bounds[i][2])
end
end
end
return population
end
function GA(populationSize::Int, chromosomeSize::Int, fitness_function::Function, bounds::Array{Tuple{Float64, Float64},1})
"""
Args
populationSize: Total number of chromosomes
chromosomeSize: How long each chromosome should be (variables in fitness function)
fitness_function: Function to determine fitness of each solution
Should take an array as an arg
Example: f(x,y,z) = x+y+z
should be f(X) = X[1]+X[2]+X[3]
in order to allow GA to take in any function with any
amount of args to the fitness function
bounds: 1D array of tuples, each tuple is the (lower, upper) bounds
for each variable. Both lower and upper need to be Float64 types
Length should match chromosomeSize
e.g: [(1.0,2.0), (3.5,4.5)]
"""
# Set recombination and mutation rate, lower and upper bound
recombRate = 0.7
mutateRate = 0.05
maxIterations = 1000
# First initialize the population
population = initialize_population(populationSize, chromosomeSize, bounds)
# Then loop for the required iterations
bestScores = Array{Float64}(0)
i = 0
while i < maxIterations
# Score and sort the population
population = score_sort_population(population, fitness_function)
push!(bestScores,population[1][end])
population = create_next_generation(population, recombRate)
population = mutate(population, mutateRate, bounds)
i += 1
end
println(bestScores)
println(population)
end
function ackley(X)
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)
end
bounds = [(-2.0,2.0), (-2.0,2.0)]
GA(25,2,ackley,bounds)
*