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)