Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parameterised type #37

Closed
wants to merge 12 commits into from
23 changes: 23 additions & 0 deletions Manifest.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# This file is machine-generated - editing it directly is not advised

[[Libdl]]
uuid = "8f399da3-3557-5675-b5ff-fb832c97cbdb"

[[LinearAlgebra]]
deps = ["Libdl"]
uuid = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"

[[Random]]
deps = ["Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"

[[Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"

[[SparseArrays]]
deps = ["LinearAlgebra", "Random"]
uuid = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"

[[Statistics]]
deps = ["LinearAlgebra", "SparseArrays"]
uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
14 changes: 14 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name = "Evolutionary"
uuid = "86b6b26d-c046-49b6-aa0b-5f0f74682bd6"
version = "0.2.0"

[deps]
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test"]
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ A Julia package for [evolutionary](http://www.scholarpedia.org/article/Evolution

## Installation

For julia 0.6 and lower, run following command

```julia
Pkg.add("Evolutionary")
```

For julia 0.7 and higher, run in the package manager mode
```
pkg> add https://github.com/wildart/Evolutionary.jl.git#v0.2.0
```

## Functionalities

#### Algorithms
Expand Down
22 changes: 2 additions & 20 deletions src/Evolutionary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,11 @@ using Random
discrete, waverage, intermediate, line,
pmx, ox1, cx, ox2, pos,
# GA selections
ranklinear, uniformranking, roulette, sus, tournament, #truncation
ranklinear, rankuniform, roulette, sus, tournament, #truncation
# Optimization methods
es, cmaes, ga

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

# Wrapping function for strategy
function strategy(; kwargs...)
Expand All @@ -30,29 +29,12 @@ using Random

# Inverse function for reversing optimization direction
function inverseFunc(f::Function)
function fitnessFunc(x::T) where {T <: Vector}
function fitnessFunc(x)
return 1.0/(f(x)+eps())
end
return fitnessFunc
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)
else
individual = ones(N)
end
return individual
end

# Collecting interim values
function keep(interim, v, vv, col)
if interim
Expand Down
40 changes: 25 additions & 15 deletions src/cmaes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,31 @@
#
using LinearAlgebra
using Statistics
function cmaes( objfun::Function, N::Int;
initPopulation::Individual = ones(N),
initStrategy::Strategy = strategy(τ = sqrt(N), τ_c = N^2, τ_σ = sqrt(N)),
function cmaes( objfun::Function, individual::T;
initParent::Union{Nothing, T} = nothing,
initStrategy::Strategy = strategy(τ = sqrt(length(individual)), τ_c = length(individual)^2, τ_σ = sqrt(length(individual))),
creation::Function = (n -> rand(eltype(T), n)),
μ::Integer = 1,
λ::Integer = 1,
iterations::Integer = 1_000,
tol::Float64 = 1e-10,
verbose = false)
verbose = false) where {T}

@assert μ < λ "Offspring population must be larger then parent population"
@assert ndims(individual) == 1 "Individual must be 1D"

# Initialize parent population
individual = getIndividual(initPopulation, N)
population = fill(individual, μ)
offspring = Array{typeof(individual)}(undef, λ)
fitpop = fill(Inf, μ)
N = length(individual)
population = Array{T}(undef, μ)
offspring = Array{T}(undef, λ)
fitness = fill(Inf, μ)
fitoff = fill(Inf, λ)

parent = copy(individual)
if !isnothing(initParent)
parent = initParent
else
parent = creation(N)
end
C = Diagonal{Float64}(I, N)
s = zeros(N)
s_σ = zeros(N)
Expand All @@ -35,7 +41,7 @@ function cmaes( objfun::Function, N::Int;
W = zeros(N, λ)

# Generation cycle
count = 0
itr = 0
while true
SqrtC = (C + C') / 2.0
try
Expand All @@ -57,7 +63,7 @@ function cmaes( objfun::Function, N::Int;
idx = sortperm(fitoff)[1:μ]
for i in 1:μ
population[i] = offspring[idx[i]]
fitpop[i] = fitoff[idx[i]]
fitness[i] = fitoff[idx[i]]
end

w = vec(mean(W[:,idx], dims=2))
Expand All @@ -71,12 +77,16 @@ function cmaes( objfun::Function, N::Int;
σ = σ*exp(((s_σ'*s_σ)[1] - N)/(2*N*sqrt(N))) # (L6)

# termination condition
count += 1
if count == iterations || σ < tol
itr += 1
if itr == iterations || σ <= tol
break
end
verbose && println("BEST: $(fitpop[1]): $(σ)")
verbose && println("BEST: $(fitness[1]): $(σ)")
end

return population[1], fitpop[1], count
return (
bestIndividual=population[1],
bestFitness=fitness[1],
itr=itr
)
end
48 changes: 26 additions & 22 deletions src/es.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,53 +10,52 @@
# Comma-selection (μ<λ must hold): parents are deterministically selected from the set of the offspring
# 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),
function es( objfun::Function, individual::T;
initPopulation::Union{Nothing, Vector{T}} = 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 = (dims -> rand(eltype(T), dims)),
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,
selection::Symbol = :plus,
iterations::Integer = N*100,
iterations::Integer = 100*prod(size(individual)),
verbose = false, debug = false,
interim = false)
interim = false) where {T}

@assert ρ <= μ "Number of parents involved in the procreation of an offspring should be no more then total number of parents"
if selection == :comma
@assert μ < λ "Offspring population must be larger then parent population"
end

store = Dict{Symbol,Any}()
dims = size(individual)

# Initialize parent population
individual = getIndividual(initPopulation, N)
population = fill(individual, μ)
population = Array{T}(undef, μ)
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] = creation(dims)
else
population[i] = initPopulation[i]
end
fitness[i] = objfun(population[i])
debug && println("INIT $(i): $(population[i]) : $(fitness[i])")
end
offspring = Array{typeof(individual)}(undef, λ)
offspring = Array{T}(undef, λ)
fitoff = fill(Inf, λ)
stgpop = fill(initStrategy, μ)
stgoff = fill(initStrategy, λ)

keep(interim, :fitness, copy(fitness), store)

# Generation cycle
count = 0
itr = 0
while true

for i in 1:λ
Expand Down Expand Up @@ -104,12 +103,17 @@ function es( objfun::Function, N::Int;
keep(interim, :fitoff, copy(fitoff), store)

# termination condition
count += 1
if count == iterations || termination(stgpop[1])
itr += 1
if itr == iterations || termination(stgpop[1])
break
end
verbose && println("BEST: $(fitness[1]): $(stgpop[1])")
end

return population[1], fitness[1], count, store
return (
bestIndividual=population[1],
bestFitness=fitness[1],
itr=itr,
store=store
)
end
60 changes: 31 additions & 29 deletions src/ga.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
# Genetic Algorithms
# ==================
# 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.
# individual: Sample data structure representing an individual
# 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 @@ -12,46 +11,43 @@
# are guaranteed to survive to the next generation.
# Floating number specifies fraction of population.
#
function ga(objfun::Function, N::Int;
initPopulation::Individual = ones(N),
lowerBounds::Union{Nothing, Vector} = nothing,
upperBounds::Union{Nothing, Vector} = nothing,
function ga(objfun::Function, individual::T;
initPopulation::Union{Nothing, Vector{T}} = nothing,
lowerBounds::Union{Nothing, Vector{T}} = nothing,
upperBounds::Union{Nothing, Vector{T}} = 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),
iterations::Integer = 100*N,
creation::Function = (dims -> rand(eltype(T), dims)),
selection::Function = ((x, n) -> 1:n),
crossover::Function = ((x, y) -> (y, x)),
mutation::Function = (x -> x),
iterations::Integer = 100*prod(size(individual)),
tol = 0.0,
tolIter = 10,
tolitr = 10,
verbose = false,
debug = false,
interim = false)
interim = false) where {T}

store = Dict{Symbol,Any}()
store = Dict{Symbol, Any}()

# Setup parameters
dims = size(individual)
elite = isa(ɛ, Int) ? ɛ : round(Int, ɛ * populationSize)
fitFunc = inverseFunc(objfun)

# Initialize population
individual = getIndividual(initPopulation, N)
fitness = zeros(populationSize)
population = Array{typeof(individual)}(undef, populationSize)
population = Array{T}(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] = creation(dims)
else
error("Cannot generate population")
population[i] = initPopulation[i]
end
fitness[i] = fitFunc(population[i])
debug && println("INIT $(i): $(population[i]) : $(fitness[i])")
Expand All @@ -60,7 +56,7 @@ function ga(objfun::Function, N::Int;
keep(interim, :fitness, copy(fitness), store)

# Generate and evaluate offspring
itr = 1
itr = 0
bestFitness = 0.0
bestIndividual = 0
fittol = 0.0
Expand Down Expand Up @@ -118,12 +114,13 @@ function ga(objfun::Function, N::Int;
keep(interim, :bestFitness, bestFitness, store)

# Verbose step
verbose && println("BEST: $(bestFitness): $(population[bestIndividual]), G: $(itr)")
verbose && println("BEST: $(bestFitness): $(population[bestIndividual]), G: $(itr)")

# Terminate:
# if fitness tolerance is met for specified number of steps
if fittol < tol
if fittolitr > tolIter
itr += 1
if fittol <= tol
if (tolitr != -1) && (fittolitr > tolitr)
break
else
fittolitr += 1
Expand All @@ -135,8 +132,13 @@ function ga(objfun::Function, N::Int;
if itr >= iterations
break
end
itr += 1
end

return population[bestIndividual], bestFitness, itr, fittol, store
return (
bestIndividual=population[bestIndividual],
bestFitness=bestFitness,
itr=itr,
fittol=fittol,
store=store
)
end
Loading