From f8f9fc44b38180badef02d262afdc4b4fab0e440 Mon Sep 17 00:00:00 2001 From: Art Wild Date: Fri, 10 Dec 2021 15:06:45 -0500 Subject: [PATCH] allow better control of RNG (part 2) - use `rng` option in DE & ES - updates tests to use `rng` option --- README.md | 2 +- src/de.jl | 5 +-- src/es.jl | 20 ++++++------ src/ga.jl | 80 +++++++++++++++++++++++++++++------------------ src/mutations.jl | 20 ++++++++++-- src/nsga2.jl | 35 +++++++-------------- test/knapsack.jl | 38 +++++++++++----------- test/moea.jl | 12 ++++--- test/n-queens.jl | 34 +++++++++++++------- test/onemax.jl | 14 +++++---- test/rastrigin.jl | 28 ++++++++--------- test/sphere.jl | 2 +- 12 files changed, 165 insertions(+), 125 deletions(-) diff --git a/README.md b/README.md index f60123e..fb2e6b8 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ pkg> add Evolutionary - Genetic Programming (TreeGP) ## Operators -a + - Mutations - ES - (an)isotropic Gaussian diff --git a/src/de.jl b/src/de.jl index f7b5f27..3b39a74 100644 --- a/src/de.jl +++ b/src/de.jl @@ -48,6 +48,7 @@ function update_state!(objfun, constraints, state, population::AbstractVector{IT Np = method.populationSize n = method.n F = method.F + rng = options.rng offspring = Array{IT}(undef, Np) @@ -61,12 +62,12 @@ function update_state!(objfun, constraints, state, population::AbstractVector{IT offspring[i] = copy(base) # println("$i => base:", offspring[i]) - targets = randexcl(1:Np, [i], 2*n) + targets = randexcl(rng, 1:Np, [i], 2*n) offspring[i] = differentiation(offspring[i], @view population[targets]; F=F) # println("$i => mutated:", offspring[i], ", targets:", targets) # recombination - offspring[i], _ = method.recombination(offspring[i], base) + offspring[i], _ = method.recombination(offspring[i], base, rng=rng) # println("$i => recombined:", offspring[i]) end diff --git a/src/es.jl b/src/es.jl index 88e37c5..7a96349 100644 --- a/src/es.jl +++ b/src/es.jl @@ -6,8 +6,8 @@ The constructor takes following keyword arguments: - `initStrategy`: an initial strategy description, (default: empty) - `recombination`: ES recombination function for population (default: `first`), see [Crossover](@ref) - `srecombination`: ES recombination function for strategies (default: `first`), see [Crossover](@ref) -- `mutation`: [Mutation](@ref) function for population (default: `first`) -- `smutation`: [Mutation](@ref) function for strategies (default: `identity`) +- `mutation`: [Mutation](@ref) function for population (default: [`nop`](@ref)) +- `smutation`: [Mutation](@ref) function for strategies (default: [`nop`](@ref)) - `μ`/`mu`: the number of parents - `ρ`/`rho`: the mixing number, ρ ≤ μ, (i.e., the number of parents involved in the procreation of an offspring) - `λ`/`lambda`: the number of offspring @@ -27,8 +27,8 @@ struct ES <: AbstractOptimizer ES(; initStrategy::AbstractStrategy = NoStrategy(), recombination::Function = first, srecombination::Function = first, - mutation::Function = ((r,s) -> r), - smutation::Function = identity, + mutation::Function = nop, + smutation::Function = nop, μ::Integer = 1, mu::Integer = μ, ρ::Integer = μ, @@ -74,6 +74,7 @@ end function update_state!(objfun, constraints, state, population::AbstractVector{IT}, method::ES, options, itr) where {IT} @unpack initStrategy,recombination,srecombination,mutation,smutation,μ,ρ,λ,selection = method evaltype = options.parallelization + rng = options.rng @assert ρ <= μ "Number of parents involved in the procreation of an offspring should be no more then total number of parents" if selection == :comma @@ -86,21 +87,21 @@ function update_state!(objfun, constraints, state, population::AbstractVector{IT for i in 1:λ # Recombine the ρ selected parents to form a recombinant individual if ρ == 1 - j = rand(1:μ) + j = rand(rng, 1:μ) recombinantStrategy = state.strategies[j] recombinant = copy(population[j]) else - idx = randperm(μ)[1:ρ] + idx = randperm(rng, μ)[1:ρ] recombinantStrategy = srecombination(state.strategies[idx]) - recombinant = recombination(population[idx]) + recombinant = recombination(population[idx]; rng=rng) end # Mutate the strategy parameter set of the recombinant - stgoff[i] = smutation(recombinantStrategy) + stgoff[i] = smutation(recombinantStrategy; rng=rng) # Mutate the objective parameter set of the recombinant using the mutated strategy parameter set # to control the statistical properties of the object parameter mutation - off = mutation(recombinant, stgoff[i]) + off = mutation(recombinant, stgoff[i]; rng=rng) # Apply constraints offspring[i] = apply!(constraints, off) @@ -136,3 +137,4 @@ function update_state!(objfun, constraints, state, population::AbstractVector{IT return false end + diff --git a/src/ga.jl b/src/ga.jl index 4fa723e..017c77c 100644 --- a/src/ga.jl +++ b/src/ga.jl @@ -58,54 +58,72 @@ function initial_state(method::GA, options, objfun, population) return GAState(N, eliteSize, minfit, fitness, copy(population[fitidx])) end -function update_state!(objfun, constraints, state, population::AbstractVector{IT}, method::GA, options, itr) where {IT} - @unpack populationSize,crossoverRate,mutationRate,ɛ,selection,crossover,mutation = method +function update_state!(objfun, constraints, state, parents::AbstractVector{IT}, method::GA, options, itr) where {IT} + populationSize = method.populationSize evaltype = options.parallelization rng = options.rng - offspring = similar(population) + offspring = similar(parents) - # Select offspring - selected = selection(state.fitpop, populationSize, rng=rng) + # select offspring + selected = method.selection(state.fitpop, populationSize, rng=rng) - # Perform mating - offidx = randperm(rng, populationSize) + # perform mating offspringSize = populationSize - state.eliteSize - for i in 1:2:offspringSize - j = (i == offspringSize) ? i-1 : i+1 - if rand(rng) < crossoverRate - offspring[i], offspring[j] = crossover(population[selected[offidx[i]]], population[selected[offidx[j]]], rng=rng) - else - offspring[i], offspring[j] = population[selected[i]], population[selected[j]] - end - end + recombine!(offspring, parents, selected, method, offspringSize) # Elitism (copy population individuals before they pass to the offspring & get mutated) fitidxs = sortperm(state.fitpop) for i in 1:state.eliteSize subs = offspringSize+i - offspring[subs] = copy(population[fitidxs[i]]) + offspring[subs] = copy(parents[fitidxs[i]]) end - # Perform mutation - for i in 1:offspringSize - if rand(rng) < mutationRate - mutation(offspring[i], rng=rng) - end - end + # perform mutation + mutate!(offspring, method, constraints, rng=rng) - # Create new generation & evaluate it - for i in 1:populationSize - population[i] = apply!(constraints, offspring[i]) - end # calculate fitness of the population - value!(objfun, state.fitpop, population) - # apply penalty to fitness - penalty!(state.fitpop, constraints, population) + evaluate!(objfun, state.fitpop, offspring, constraints) - # find the best individual + # select the best individual minfit, fitidx = findmin(state.fitpop) - state.fittest = population[fitidx] + state.fittest = parents[fitidx] state.fitness = state.fitpop[fitidx] + + # replace population + parents .= offspring return false end + +function recombine!(offspring, parents, selected, method, n=length(selected); + rng::AbstractRNG=Random.GLOBAL_RNG) + mates = ((i,i == n ? i-1 : i+1) for i in 1:2:n) + for (i,j) in mates + p1, p2 = parents[selected[i]], parents[selected[j]] + if rand(rng) < method.crossoverRate + offspring[i], offspring[j] = method.crossover(p1, p2, rng=rng) + else + offspring[i], offspring[j] = p1, p2 + end + end + +end + +function mutate!(population, method, constraints; + rng::AbstractRNG=Random.GLOBAL_RNG) + n = length(population) + for i in 1:n + if rand(rng) < method.mutationRate + method.mutation(population[i], rng=rng) + end + apply!(constraints, population[i]) + end +end + +function evaluate!(objfun, fitness, population, constraints) + # calculate fitness of the population + value!(objfun, fitness, population) + # apply penalty to fitness + penalty!(fitness, constraints, population) +end + diff --git a/src/mutations.jl b/src/mutations.jl index 1ad2f74..92d1789 100644 --- a/src/mutations.jl +++ b/src/mutations.jl @@ -4,6 +4,13 @@ # Evolutionary mutations # ====================== +""" + nop(s::AbstractStrategy) + +This is a dummy mutation operator that does not change recombinant. +""" +nop(recombinant::AbstractVector, s::AbstractStrategy; kwargs...) = recombinant + """ gaussian(x, s::IsotropicStrategy) @@ -57,6 +64,14 @@ end # Strategy mutation operators # =========================== +""" + nop(s::AbstractStrategy) + +This is a dummy operator that does not change strategy. +""" +nop(s::AbstractStrategy; kwargs...) = s + + """ gaussian(s::IsotropicStrategy) @@ -411,7 +426,7 @@ end # ====================== """ - subtree(method::TreeGP; growth::Real = 0.1) + subtree(method::TreeGP; growth::Real = 0.0) Returns an in-place expression mutation function that performs mutation of an arbitrary expression subtree with a randomly generated one [^5]. @@ -523,6 +538,7 @@ function swap!(v::T, from::Int, to::Int) where {T <: AbstractVector} end function mutationwrapper(gamutation::Function) - wrapper(recombinant::T, s::S) where {T <: AbstractVector, S <: AbstractStrategy} = gamutation(recombinant) + wrapper(recombinant::T, s::S; kwargs...) where {T <: AbstractVector, S <: AbstractStrategy} = gamutation(recombinant; kwargs...) return wrapper end + diff --git a/src/nsga2.jl b/src/nsga2.jl index 64faaf9..beefbea 100644 --- a/src/nsga2.jl +++ b/src/nsga2.jl @@ -67,36 +67,22 @@ function initial_state(method::NSGA2, options, objfun, parents) end function update_state!(objfun, constraints, state, parents::AbstractVector{IT}, method::NSGA2, options, itr) where {IT} - @unpack populationSize,crossoverRate,mutationRate,selection,crossover,mutation = method + populationSize = method.populationSize + rng = options.rng - # Select offspring + # select offspring specFit = StackView(state.ranks, state.crowding, dims=1) - selected = selection(view(specFit, :, 1:populationSize), populationSize) - - # Perform mating - offidx = randperm(populationSize) - for i in 1:2:populationSize - j = (i == populationSize) ? i-1 : i+1 - if rand() < crossoverRate - state.offspring[i], state.offspring[j] = crossover(parents[selected[offidx[i]]], parents[selected[offidx[j]]]) - else - state.offspring[i], state.offspring[j] = parents[selected[i]], parents[selected[j]] - end - end + selected = method.selection(view(specFit,:,1:populationSize), populationSize; rng=rng) - # Perform mutation - for i in 1:populationSize - if rand() < mutationRate - mutation(state.offspring[i]) - end - apply!(constraints, state.offspring[i]) - end + # perform mating + recombine!(state.offspring, parents, selected, method) + + # perform mutation + mutate!(state.offspring, method, constraints, rng=rng) # calculate fitness of the offspring offfit = @view state.fitpop[:, populationSize+1:end] - value!(objfun, offfit, state.offspring) - # apply penalty to fitness - penalty!(offfit, constraints, state.offspring) + evaluate!(objfun, offfit, state.offspring, constraints) # calculate ranks & crowding for population F = nondominatedsort!(state.ranks, state.fitpop) @@ -117,6 +103,7 @@ function update_state!(objfun, constraints, state, parents::AbstractVector{IT}, state.fittest = state.population[F[1]] # and keep their fitness state.fitness = state.fitpop[:,F[1]] + # construct new parent population parents .= state.population[fitidx] diff --git a/test/knapsack.jl b/test/knapsack.jl index d5f262b..13a2dd9 100644 --- a/test/knapsack.jl +++ b/test/knapsack.jl @@ -1,26 +1,27 @@ @testset "Knapsack" begin - Random.seed!(42) + rng = StableRNG(42) mass = [1, 5, 3, 7, 2, 10, 5] utility = [1, 3, 5, 2, 5, 8, 3] fitnessFun = n -> (sum(mass .* n) <= 20) ? sum(utility .* n) : 0 - initpop = collect(rand(Bool,length(mass))) - + Random.seed!(rng, 42) + initpop = ()->rand(rng, Bool, length(mass)) result = Evolutionary.optimize( x -> -fitnessFun(x), initpop, GA( - selection = tournament(3), - mutation = inversion, + selection = roulette, + mutation = swap2, crossover = SPX, - mutationRate = 0.9, - crossoverRate = 0.2, - ɛ = 0.1, # Elitism + mutationRate = 0.05, + crossoverRate = 0.85, + ɛ = 0.05, # Elitism populationSize = 100, - )); - println("GA:RLT:INV:SP (-objfun) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") + ), Evolutionary.Options(show_trace=false, rng=rng) + ); + println("GA:RLT:SWP:SPX (-objfun) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") @test abs(Evolutionary.minimum(result)) == 21. @test sum(mass .* Evolutionary.minimizer(result)) <= 20 @@ -33,21 +34,22 @@ uc = [20] # upper bound for constraint function con = WorstFitnessConstraints(Int[], Int[], lc, uc, cf) - initpop = BitVector(rand(Bool,length(mass))) - + Random.seed!(rng, 42) + initpop = BitVector(rand(rng, Bool, length(mass))) result = Evolutionary.optimize( x -> -fitnessFun(x), con, initpop, GA( - selection = roulette, - mutation = flip, + selection = tournament(3), + mutation = inversion, crossover = SPX, - mutationRate = 0.9, - crossoverRate = 0.1, + mutationRate = 0.05, + crossoverRate = 0.85, ɛ = 0.1, # Elitism populationSize = 50, - )); - println("GA:RLT:FL:SP (-objfun) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") + ), Evolutionary.Options(show_trace=false, rng=rng, successive_f_tol=1)); + println("GA:TRN3:INV:SPX (-objfun) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") @test abs(Evolutionary.minimum(result)) == 21. @test sum(mass .* Evolutionary.minimizer(result)) <= 20 + end diff --git a/test/moea.jl b/test/moea.jl index e796229..a6805bb 100644 --- a/test/moea.jl +++ b/test/moea.jl @@ -1,5 +1,8 @@ @testset "Multi-objective EA" begin + rng = StableRNG(42) + opts = Evolutionary.Options(rng=rng) + # domination P =[ [0,0,0], [0,1,0], [-1,0,0], [0,-1,0] ] @test Evolutionary.dominate(P[1], P[2]) == 1 @@ -20,21 +23,20 @@ # Schaffer F2 schafferf2(x::AbstractVector) = [ x[1]^2, (x[1]-2)^2 ] - Random.seed!(42) - result = Evolutionary.optimize(schafferf2, [5.0], NSGA2()) + Random.seed!(rng, 1) + result = Evolutionary.optimize(schafferf2, [5.0], NSGA2(), opts) println("NSGA2:2RLT:SBX:PLM => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") @test isnan(Evolutionary.minimum(result)) mvs = vcat(Evolutionary.minimizer(result)...) @test 1-sum(0 .<= mvs .<= 2)/length(mvs) ≈ 0.0 atol=0.1 # PO ∈ [0,2] - function schafferf2!(F, x::AbstractVector) # in-place update F[1] = x[1]^2 F[2] = (x[1]-2)^2 F end - Random.seed!(42) - result = Evolutionary.optimize(schafferf2!, zeros(2), [5.0], NSGA2()) + Random.seed!(rng, 2) + result = Evolutionary.optimize(schafferf2!, zeros(2), [5.0], NSGA2(), opts) println("NSGA2:2RLT:SBX:PLM => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") @test isnan(Evolutionary.minimum(result)) mvs = vcat(Evolutionary.minimizer(result)...) diff --git a/test/n-queens.jl b/test/n-queens.jl index 8b1ba08..43dfc20 100644 --- a/test/n-queens.jl +++ b/test/n-queens.jl @@ -1,8 +1,9 @@ @testset "n-Queens" begin + rng = StableRNG(42) N = 8 P = 100 - generatePositions = ()->collect(1:N)[randperm(N)] + generatePositions = ()->randperm(rng, N) # Vector of N cols filled with numbers from 1:N specifying row position function nqueens(queens::Vector{Int}) @@ -23,28 +24,36 @@ @test nqueens([3,1,2]) == 1 # Testing: GA solution with various mutations - for muts in [inversion, insertion, swap2, scramble, shifting] + for muts in [inversion, insertion, swap2, scramble, shifting], + xo in [PMX, OX1, CX, OX2, POS] + Random.seed!(rng, 42) result = Evolutionary.optimize(nqueens, generatePositions, GA( populationSize = P, - selection = susinv, - crossover = PMX, - mutation = muts - )); + selection = tournament(5), + crossover = xo, + crossoverRate = 0.89, + mutation = muts, + mutationRate = 0.06, + ), Evolutionary.Options(rng=rng, successive_f_tol=30)); # show(result) - println("GA:PMX:$(string(muts))(N=$(N), P=$(P)) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") - @test nqueens(Evolutionary.minimizer(result)) == 0 - @test Evolutionary.iterations(result) < 100 # Test early stopping + println("GA:$(string(xo)):$(string(muts))(N=$(N), P=$(P)) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") + @test nqueens(Evolutionary.minimizer(result)) <= 1 + @test Evolutionary.iterations(result) < 500 # Test early stopping end # Testing: ES + μ = 20 for muts in [inversion, insertion, swap2, scramble, shifting] for sel in [:plus, :comma] - μ = 20 + Random.seed!(rng, 42) result = Evolutionary.optimize(nqueens, generatePositions, - ES(mutation = mutationwrapper(muts), μ=μ, ρ=1, λ=P, selection=sel), - Evolutionary.Options(show_trace=false)) + ES( + mutation = mutationwrapper(muts), + μ=μ, ρ=1, λ=P, + selection=sel + ), Evolutionary.Options(show_trace=false, rng=rng)) println("($μ$(sel == :plus ? "+" : ",")$(P))-ES:$(string(muts)) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") # show(result) @test nqueens(Evolutionary.minimizer(result)) == 0 @@ -52,3 +61,4 @@ end end + diff --git a/test/onemax.jl b/test/onemax.jl index d300b18..05cf2f7 100644 --- a/test/onemax.jl +++ b/test/onemax.jl @@ -1,8 +1,10 @@ @testset "OneMax" begin + rng = StableRNG(42) + # Initial population N = 100 - initpop = (() -> BitArray(rand(Bool, N))) + initpop = (() -> BitArray(rand(rng, Bool, N))) function Evolutionary.trace!(record::Dict{String,Any}, objfun, state, population, method::GA, options) idx = sortperm(state.fitpop) @@ -14,15 +16,15 @@ x -> -sum(x), # Function to MINIMIZE initpop, GA( - selection = uniformranking(5), + selection = tournament(3), mutation = flip, crossover = TPX, - mutationRate = 0.6, - crossoverRate = 0.2, + mutationRate = 0.05, + crossoverRate = 0.85, populationSize = N, ), - Evolutionary.Options(successive_f_tol=20, iterations=1500, store_trace=true)); - println("GA:UR(3):FLP:SP (OneMax: 1/sum) => F: $(minimum(res)), C: $(Evolutionary.iterations(res))") + Evolutionary.Options(rng=rng, store_trace=true)); + println("GA:TOUR(3):FLP:TPX (OneMax: 1/sum) => F: $(minimum(res)), C: $(Evolutionary.iterations(res))") @test sum(Evolutionary.minimizer(res)) >= N-3 @test abs(minimum(res)) >= N-3 @test Evolutionary.trace(res)[end].metadata["fitpop"][1] == minimum(res) diff --git a/test/rastrigin.jl b/test/rastrigin.jl index cad046c..33a837a 100644 --- a/test/rastrigin.jl +++ b/test/rastrigin.jl @@ -1,5 +1,7 @@ @testset "Rastrigin" begin + rng = StableRNG(42) + function test_result(result::Evolutionary.EvolutionaryOptimizationResults, N::Int, tol::Float64) fitness = minimum(result) extremum = Evolutionary.minimizer(result) @@ -21,11 +23,11 @@ # Parameters N = 3 P = 100 - initState = ()->rand(N) + initState = ()->rand(rng, N) # Testing: (μ/μ_I,λ)-σ-Self-Adaptation-ES # with non-isotropic mutation operator y' := y + (σ_1 N_1(0, 1), ..., σ_N N_N(0, 1)) - Random.seed!(9874984737486) + Random.seed!(rng, 42) m = ES(mu = 15, lambda = P) @test m.μ == 15 @test m.λ == P @@ -37,7 +39,7 @@ mutation = gaussian, smutation = gaussian, selection=:comma, μ = 15, λ = P - ),Evolutionary.Options(iterations=1000, show_trace=false) + ),Evolutionary.Options(iterations=1000, abstol=1e-10, rng=rng) ) println("(15,$(P))-σ-SA-ES => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") test_result(result, N, 1e-1) @@ -48,17 +50,16 @@ test_result(result, N, 1e-1) # Testing: GA - Random.seed!(9874984737486) m = GA(epsilon = 10.0) @test m.ɛ == 10 - selections = [:roulette=>rouletteinv, :sus=>susinv, :rank=>ranklinear(1.5)] - crossovers = [:discrete=>DC, :intermediate0=>IC(0.), :intermediate0_5=>IC(0.5), :line=>LC(0.1)] - mutations = [:domrng0_5=>BGA(fill(0.5,N)), :uniform=>uniform(2.0), :gaussian=>gaussian(0.3)] + selections = [:roulette=>rouletteinv, :sus=>susinv, :tourn=>tournament(2)] + crossovers = [:discrete=>DC, :intermediate0=>IC(0.), :intermediate0_5=>IC(0.5), :line=>LC(0.1), :avg=>AX, :heuristic=>HX, :laplace=>LX(), :simbin=>SBX()] + mutations = [:domrng0_5=>BGA(fill(0.5,N)), :uniform=>uniform(), :plm=>PLM()] @testset "GA settings" for (sn,ss) in selections, (xn,xovr) in crossovers, (mn,ms) in mutations - xn == :discrete && (mn == :uniform || mn == :gaussian) && continue # bad combination - xn == :line && mn == :gaussian && continue # bad combination + (xn ∈ [:heuristic, :discrete, :avg]) && mn == :uniform && continue # bad combination + Random.seed!(rng, 42) result = Evolutionary.optimize( rastrigin, initState, GA( populationSize = P, @@ -66,18 +67,17 @@ selection = ss, crossover = xovr, mutation = ms - ), Evolutionary.Options(iterations=1000, successive_f_tol=25) + ), Evolutionary.Options(abstol=1e-10, successive_f_tol=25, rng=rng) ) println("GA:$(sn):$(xn):$(mn)(N=$(N),P=$(P),x=.8,μ=.1,ɛ=0.1) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") - test_result(result, N, 1e-1) + test_result(result, N, 0.2) end # Testing: DE - Random.seed!(9874984737486) selections = [:rand=>random, :perm=>permutation, :rndoff=>randomoffset, :best=>best] mutations = [:exp=>EXPX(0.5), :bin=>BINX(0.5)] - @testset "DE settings" for (sn,ss) in selections, (mn,ms) in mutations, n in 1:2 + Random.seed!(rng, 1) result = Evolutionary.optimize( rastrigin, initState, DE( populationSize = P, @@ -85,7 +85,7 @@ selection = ss, recombination = ms, F = 0.9 - ) + ), Evolutionary.Options(rng=rng) ) println("DE/$sn/$n/$mn(F=0.9,Cr=0.5) => F: $(minimum(result)), C: $(Evolutionary.iterations(result))") test_result(result, N, 1e-2) diff --git a/test/sphere.jl b/test/sphere.jl index 08bd17b..df085ec 100644 --- a/test/sphere.jl +++ b/test/sphere.jl @@ -48,7 +48,7 @@ mutationRate = 0.15, ɛ = 0.1, selection = susinv, - crossover = intermediate(0.25), + crossover = IC(0.25), mutation = BGA(fill(0.5,N)), )); # show(result)