Skip to content

Commit

Permalink
Separated out functionalities from initPopulation
Browse files Browse the repository at this point in the history
`initPopulation` was a heavily overloaded parameter, with a variety of
types corresponding to a range of possible behaviours. This commit:
- Removes the option to pass a vector representing the search space.
This is specific and unclear, and behaviour can easily be easily
represented by one of the other options
- Restricts `initPopulation` to be a vector of individuals (where an
individual is a single member of the population)
- Adds a new parameter `creation` to represent a function to create an
individual.

Default behaviour has not been changed, but is now represented by a
creation function.
  • Loading branch information
aahaselgrove committed Oct 16, 2019
1 parent 6dbab80 commit 21e958d
Show file tree
Hide file tree
Showing 7 changed files with 40 additions and 52 deletions.
19 changes: 6 additions & 13 deletions src/Evolutionary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ using Random
es, cmaes, ga

const Strategy = Dict{Symbol,Any}
const Individual = Union{Vector, Matrix, Function, Nothing}

# Wrapping function for strategy
function strategy(; kwargs...)
Expand All @@ -37,20 +36,14 @@ using Random
end

# Obtain individual
function getIndividual(init::Individual, N::Int)
if isa(init, Vector)
@assert length(init) == N "Dimensionality of initial population must be $(N)"
individual = init
elseif isa(init, Matrix)
@assert size(init, 1) == N "Dimensionality of initial population must be $(N)"
populationSize = size(init, 2)
individual = init[:, 1]
elseif isa(init, Function) # Creation function
individual = init(N)
function getIndividual(init::Union{Nothing, Vector}, creation::Function, N::Int)
if !isnothing(init)
individual = init[1]
@assert length(individual) == N "Dimensionality of initial population must be $(N)"
else
individual = ones(N)
individual = creation(N)
end
return individual
return individual
end

# Collecting interim values
Expand Down
5 changes: 3 additions & 2 deletions src/cmaes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
using LinearAlgebra
using Statistics
function cmaes( objfun::Function, N::Int;
initPopulation::Individual = ones(N),
initPopulation::Union{Nothing, Vector} = nothing,
initStrategy::Strategy = strategy= sqrt(N), τ_c = N^2, τ_σ = sqrt(N)),
creation::Function = (n -> rand(n)),
μ::Integer = 1,
λ::Integer = 1,
iterations::Integer = 1_000,
Expand All @@ -20,7 +21,7 @@ function cmaes( objfun::Function, N::Int;
@assert μ < λ "Offspring population must be larger then parent population"

# Initialize parent population
individual = getIndividual(initPopulation, N)
individual = getIndividual(initPopulation, creation, N)
population = fill(individual, μ)
offspring = Array{typeof(individual)}(undef, λ)
fitpop = fill(Inf, μ)
Expand Down
25 changes: 12 additions & 13 deletions src/es.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@
# Plus-selection: parents are deterministically selected from the set of both the parents and offspring
#
function es( objfun::Function, N::Int;
initPopulation::Individual = ones(N),
initPopulation::Union{Nothing, Vector} = nothing,
initStrategy::Strategy = strategy(),
recombination::Function = (rs->rs[1]),
srecombination::Function = (ss->ss[1]),
mutation::Function = ((r,m)->r),
smutation::Function = (s->s),
termination::Function = (x->false),
creation::Function = (n -> rand(n)),
recombination::Function = (rs -> rs[1]),
srecombination::Function = (ss -> ss[1]),
mutation::Function = ((r, m) -> r),
smutation::Function = (s -> s),
termination::Function = (x -> false),
μ::Integer = 1,
ρ::Integer = μ,
λ::Integer = 1,
Expand All @@ -34,16 +35,14 @@ function es( objfun::Function, N::Int;
store = Dict{Symbol,Any}()

# Initialize parent population
individual = getIndividual(initPopulation, N)
individual = getIndividual(initPopulation, creation, N)
population = fill(individual, μ)
fitness = zeros(μ)
for i in 1:μ
if isa(initPopulation, Vector)
population[i] = initPopulation.*rand(N)
elseif isa(initPopulation, Matrix)
population[i] = initPopulation[:, i]
else # Creation function
population[i] = initPopulation(N)
if !isnothing(initPopulation)
population[i] = initPopulation[i]
else
population[i] = creation(N)
end
fitness[i] = objfun(population[i])
debug && println("INIT $(i): $(population[i]) : $(fitness[i])")
Expand Down
24 changes: 10 additions & 14 deletions src/ga.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
# ==================
# objfun: Objective fitness function
# N: Search space dimensionality
# initPopulation: Search space dimension ranges as a vector, or initial population values as matrix,
# or generation function which produce individual population entities.
# initPopulation: Initial population values as matrix
# populationSize: Size of the population
# crossoverRate: The fraction of the population at the next generation, not including elite children,
# that is created by the crossover function.
Expand All @@ -13,16 +12,17 @@
# Floating number specifies fraction of population.
#
function ga(objfun::Function, N::Int;
initPopulation::Individual = ones(N),
initPopulation::Union{Nothing, Vector} = nothing,
lowerBounds::Union{Nothing, Vector} = nothing,
upperBounds::Union{Nothing, Vector} = nothing,
populationSize::Int = 50,
crossoverRate::Float64 = 0.8,
mutationRate::Float64 = 0.1,
ɛ::Real = 0,
selection::Function = ((x,n)->1:n),
crossover::Function = ((x,y)->(y,x)),
mutation::Function = (x->x),
creation::Function = (n -> rand(n)),
selection::Function = ((x, n) -> 1:n),
crossover::Function = ((x, y) -> (y, x)),
mutation::Function = (x -> x),
iterations::Integer = 100*N,
tol = 0.0,
tolIter = 10,
Expand All @@ -37,21 +37,17 @@ function ga(objfun::Function, N::Int;
fitFunc = inverseFunc(objfun)

# Initialize population
individual = getIndividual(initPopulation, N)
individual = getIndividual(initPopulation, creation, N)
fitness = zeros(populationSize)
population = Array{typeof(individual)}(undef, populationSize)
offspring = similar(population)

# Generate population
for i in 1:populationSize
if isa(initPopulation, Vector)
population[i] = initPopulation.*rand(eltype(initPopulation), N)
elseif isa(initPopulation, Matrix)
population[i] = initPopulation[:, i]
elseif isa(initPopulation, Function)
population[i] = initPopulation(N) # Creation function
if !isnothing(initPopulation)
population[i] = initPopulation[i]
else
error("Cannot generate population")
population[i] = creation(N)
end
fitness[i] = fitFunc(population[i])
debug && println("INIT $(i): $(population[i]) : $(fitness[i])")
Expand Down
6 changes: 3 additions & 3 deletions test/knapsack.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
return (total_mass <= 20) ? sum(utility .* n) : 0
end

initpop = collect(rand(Bool,length(mass)))
creation = n -> rand(Bool, n)

best, invbestfit, generations, tolerance, history = ga(
x -> 1 / fitness(x), # Function to MINIMISE
length(initpop), # Length of chromosome
initPopulation = initpop,
length(mass), # Length of chromosome
creation = creation,
selection = roulette, # Options: sus
mutation = inversion, # Options:
crossover = singlepoint, # Options:
Expand Down
4 changes: 2 additions & 2 deletions test/n-queens.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
# Testing: GA solution with various mutations
for muts in [inversion, insertion, swap2, scramble, shifting]
result, fitness, cnt = ga(nqueens, N;
initPopulation = generatePositions,
populationSize = P,
creation = generatePositions,
selection = sus,
crossover = pmx,
mutation = muts)
Expand All @@ -38,7 +38,7 @@
# Testing: ES
for muts in [inversion, insertion, swap2, scramble, shifting]
result, fitness, cnt = es(nqueens, N;
initPopulation = generatePositions,
creation = generatePositions,
mutation = mutationwrapper(muts),
μ = 15, ρ = 1, λ = P)
println("(15+$(P))-ES:$(string(muts)) => F: $(fitness), C: $(cnt), OBJ: $(result)")
Expand Down
9 changes: 4 additions & 5 deletions test/rosenbrock.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
# Testing: (15/15+100)-σ-Self-Adaptation-ES
# with isotropic mutation operator y' := y + σ(N_1(0, 1), ..., N_N(0, 1))
result, fitness, cnt = es(rosenbrock, N;
initPopulation = [.5, .5],
initStrategy = strategy= 1.0, τ = 1/sqrt(2*N)),
initStrategy = strategy= 1.0, τ = 1/sqrt(2*N)),
creation = (n -> 0.5 .* rand(n)),
recombination = average, srecombination = averageSigma1,
mutation = isotropic, smutation = isotropicSigma,
μ = 15, λ = 100, iterations = 1000)
Expand All @@ -36,7 +36,7 @@
# Testing: (15/15+100)-σ-Self-Adaptation-ES
# with non-isotropic mutation operator y' := y + (σ_1 N_1(0, 1), ..., σ_N N_N(0, 1))
result, fitness, cnt = es(rosenbrock, N;
initPopulation = rand(N,25),
initPopulation = [rand(N) for _ in 1:15],
initStrategy = strategy= .5ones(N), τ = 1/sqrt(2*N), τ0 = 1/sqrt(N)),
recombination = average, srecombination = averageSigmaN,
mutation = anisotropic, smutation = anisotropicSigma,
Expand All @@ -52,9 +52,8 @@

# Testing: GA
result, fitness, cnt = ga(rosenbrock, N;
initPopulation = (n -> rand(n)),
populationSize = 100,
ɛ = 0.1,
ɛ = 0.1,
selection = sus,
crossover = intermediate(0.25),
mutation = domainrange(fill(0.5,N)))
Expand Down

0 comments on commit 21e958d

Please sign in to comment.