Skip to content

Commit e6b997b

Browse files
committed
allow better control over used RNG (part 1)
- added `rng` parameter to `Options` - wired calls that use RNG to use the option `rng` parameter in GA & GP
1 parent b135548 commit e6b997b

File tree

5 files changed

+39
-32
lines changed

5 files changed

+39
-32
lines changed

src/api/types.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ There are following options available:
3636
callback::TCallback = nothing
3737
time_limit::Float64 = NaN
3838
parallelization::Symbol = :serial
39+
rng::AbstractRNG = Random.GLOBAL_RNG
3940
end
4041
function show(io::IO, o::Options)
4142
for k in fieldnames(typeof(o))

src/ga.jl

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,19 +61,19 @@ end
6161
function update_state!(objfun, constraints, state, population::AbstractVector{IT}, method::GA, options, itr) where {IT}
6262
@unpack populationSize,crossoverRate,mutationRate,ɛ,selection,crossover,mutation = method
6363
evaltype = options.parallelization
64-
64+
rng = options.rng
6565
offspring = similar(population)
6666

6767
# Select offspring
68-
selected = selection(state.fitpop, populationSize)
68+
selected = selection(state.fitpop, populationSize, rng=rng)
6969

7070
# Perform mating
71-
offidx = randperm(populationSize)
71+
offidx = randperm(rng, populationSize)
7272
offspringSize = populationSize - state.eliteSize
7373
for i in 1:2:offspringSize
7474
j = (i == offspringSize) ? i-1 : i+1
75-
if rand() < crossoverRate
76-
offspring[i], offspring[j] = crossover(population[selected[offidx[i]]], population[selected[offidx[j]]])
75+
if rand(rng) < crossoverRate
76+
offspring[i], offspring[j] = crossover(population[selected[offidx[i]]], population[selected[offidx[j]]], rng=rng)
7777
else
7878
offspring[i], offspring[j] = population[selected[i]], population[selected[j]]
7979
end
@@ -88,8 +88,8 @@ function update_state!(objfun, constraints, state, population::AbstractVector{IT
8888

8989
# Perform mutation
9090
for i in 1:offspringSize
91-
if rand() < mutationRate
92-
mutation(offspring[i])
91+
if rand(rng) < mutationRate
92+
mutation(offspring[i], rng=rng)
9393
end
9494
end
9595

src/gp.jl

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ function randterm(rng::AbstractRNG, t::TreeGP)
5555
if isa(term, Symbol) || isa(term, Real)
5656
term
5757
elseif isa(term, Function)
58-
term()
58+
term(rng) # terminal functions must accept RNG as an argument
5959
else
6060
# Code shouldn't reach branch but left as a catchall
6161
dim = t.terminals[term]
@@ -97,10 +97,11 @@ rand(t::TreeGP, maxdepth::Int=2; kwargs...) =
9797
9898
Initialize a random population of expressions derived from `expr`.
9999
"""
100-
function initial_population(m::TreeGP, expr::Union{Expr,Nothing}=nothing)
100+
function initial_population(m::TreeGP, expr::Union{Expr,Nothing}=nothing;
101+
rng::AbstractRNG=Random.GLOBAL_RNG)
101102
n = population_size(m)
102103
return [
103-
expr === nothing ? rand(m, m.maxdepth, mindepth=m.mindepth) : deepcopy(expr)
104+
expr === nothing ? rand(rng, m, m.maxdepth, mindepth=m.mindepth) : deepcopy(expr)
104105
for i in 1:n
105106
]
106107
end
@@ -129,8 +130,13 @@ function update_state!(objfun, constraints, state::GPState, population::Abstract
129130
end
130131

131132
# Custom optimization call
132-
function optimize(f, method::TreeGP, options::Options = Options(;default_options(method)...))
133-
method.optimizer.mutation = subtree(method)
134-
method.optimizer.crossover = crosstree
135-
optimize(f, NoConstraints(), nothing, method, options)
133+
function optimize(f, mthd::TreeGP, options::Options = Options(;default_options(mthd)...))
134+
optimize(f, mthd, initial_population(mthd, rng=options.rng), options)
136135
end
136+
137+
function optimize(f, mthd::TreeGP, population, options::Options = Options(;default_options(mthd)...))
138+
mthd.optimizer.mutation = subtree(mthd)
139+
mthd.optimizer.crossover = crosstree
140+
optimize(f, NoConstraints(), mthd, population, options)
141+
end
142+

test/gp.jl

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
@testset "Genetic Programming" begin
2-
Random.seed!(9874984737486);
2+
rng = StableRNG(42)
3+
34
pop = 10
45
terms = Terminal[:x, :y, rand]
56
funcs = Function[+,-,*,/]
@@ -16,18 +17,18 @@
1617
@test_skip summary(t) == "TreeGP[P=10,Parameter[x,y],Function[*, +, /, -]]"
1718

1819
# population initialization
19-
popexp = Evolutionary.initial_population(t);
20+
popexp = Evolutionary.initial_population(t, rng=rng);
2021
@test length(popexp) == pop
2122
popexp = Evolutionary.initial_population(t, :(x + 1));
2223
@test popexp[1] == :(x + 1)
2324

2425
# recursive helper functions
25-
Random.seed!(9874984737482)
26-
gt = rand(TreeGP(pop, terms, funcs, maxdepth=2, initialization=:grow), 3)
26+
Random.seed!(rng, 1)
27+
gt = rand(rng, TreeGP(pop, terms, funcs, maxdepth=2, initialization=:grow), 3)
2728
@test Evolutionary.nodes(gt) < 15
2829
@test Evolutionary.height(gt) <= 3
2930
@test length(gt) < 15
30-
ft = rand(TreeGP(pop, terms, funcs, maxdepth=2, initialization=:full), 3)
31+
ft = rand(rng, TreeGP(pop, terms, funcs, maxdepth=2, initialization=:full), 3)
3132
@test Evolutionary.nodes(ft) == 15
3233
@test Evolutionary.height(ft) == 3
3334
@test length(ft) == 15
@@ -57,7 +58,7 @@
5758

5859
# evaluation
5960
ex = Expr(:call, +, 1, :x) |> Evolutionary.Expression
60-
xs = rand(10)
61+
xs = rand(rng, 10)
6162
@test ex(xs[1]) == xs[1]+1
6263
@test ex.(xs) == xs.+1
6364
io = IOBuffer()
@@ -67,24 +68,23 @@
6768
depth = 5
6869
fitfun(x) = x*x + x + 1.0
6970
function fitobj(expr)
70-
rg = -5.0:0.5:5.0
71+
rg = -5.0:0.1:5.0
7172
ex = Evolutionary.Expression(expr)
72-
sum(v->isnan(v) ? 1.0 : v, abs2.(fitfun.(rg) - ex.(rg)) )/length(rg) |> sqrt
73+
sum(v->isnan(v) ? 1.0 : v, abs2.(fitfun.(rg) - ex.(rg)) )/length(rg)
7374
end
7475

75-
Random.seed!(9874984737426);
7676
res = Evolutionary.optimize(fitobj,
7777
TreeGP(50, Terminal[:x, randn], Function[+,-,*,Evolutionary.pdiv],
7878
mindepth=1,
7979
maxdepth=depth,
8080
optimizer = GA(
81-
selection = tournament(5),
81+
selection = tournament(4),
8282
ɛ = 0.1,
83-
mutationRate = 0.85,
83+
mutationRate = 0.8,
8484
crossoverRate = 0.2,
8585
),
86-
)
86+
), Evolutionary.Options(rng=StableRNG(1))
8787
)
88-
@test minimum(res) < 1.1
88+
@test minimum(res) < 1
8989

9090
end

test/rosenbrock.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,28 @@
5252
c = PenaltyConstraints(100.0, fill(0.0, 5N), Float64[], [1.0], [1.0], con_c!)
5353
result = Evolutionary.optimize(rosenbrock, c, (() -> rand(5N)), CMAES(mu = 40, lambda = 100))
5454
println("(5/5,100)-CMA-ES [penalty] => F: $(minimum(result)), C: $(Evolutionary.iterations(result))")
55-
@test Evolutionary.minimizer(result) |> sum 1.0 atol=1e-1
55+
@test Evolutionary.minimizer(result) |> sum 1.0 atol=0.1
5656

5757
c = PenaltyConstraints(100.0, fill(0.0, 2N), fill(0.5, 2N), [1.0], [1.0], con_c!)
5858
result = Evolutionary.optimize(rosenbrock, c, (() -> rand(2N)), CMAES(mu = 8, lambda = 100))
5959
println("(5/5,100)-CMA-ES [penalty] => F: $(minimum(result)), C: $(Evolutionary.iterations(result))")
60-
@test Evolutionary.minimizer(result) |> sum 1.0 atol=1e-1
60+
@test Evolutionary.minimizer(result) |> sum 1.0 atol=0.1
6161
@test all(0.0 <= x+0.01 && x-0.01 <= 0.5 for x in abs.(Evolutionary.minimizer(result)))
6262

6363
# Testing: GA
6464
m = GA(
6565
populationSize = 100,
6666
ɛ = 0.1,
6767
selection = rouletteinv,
68-
crossover = intermediate(0.25),
68+
crossover = IC(0.2),
6969
mutation = BGA(fill(0.5,N))
7070
)
7171
result = Evolutionary.optimize(rosenbrock, (() -> rand(N)), m)
7272
println("GA(p=100,x=0.8,μ=0.1,ɛ=0.1) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))")
73-
test_result(result, N, 1e-1)
73+
test_result(result, N, 0.1)
7474
result = Evolutionary.optimize(rosenbrock, BoxConstraints(0.0, 0.5, N), (() -> rand(N)), m)
7575
println("GA(p=100,x=0.8,μ=0.1,ɛ=0.1)[box] => F: $(minimum(result)), C: $(Evolutionary.iterations(result))")
76-
@test Evolutionary.minimizer(result) [0.5, 0.25] atol=1e-1
76+
@test Evolutionary.minimizer(result) [0.5, 0.25] atol=0.1
7777

7878
# Testing: DE
7979
result = Evolutionary.optimize(rosenbrock, (() -> rand(N)), DE(populationSize = 100))

0 commit comments

Comments
 (0)