|
| 1 | +""" |
| 2 | +Base class for factories of optimizers for a specific problem. |
| 3 | +""" |
| 4 | +abstract type OptimizerFactory{P <: OptimizationProblem} end |
| 5 | + |
| 6 | +problem(factory::OptimizerFactory) = factory.problem |
| 7 | + |
| 8 | +const OptController_DefaultParameters = ParamsDict( |
| 9 | + :MaxTime => 60.0, |
| 10 | + :MaxSteps => 10^8, |
| 11 | + :TraceMode => :compact, |
| 12 | + :TraceInterval => 5.0, |
| 13 | + :RecoverResults => false, |
| 14 | + :SaveTrace => false, |
| 15 | +) |
| 16 | + |
| 17 | +function generate_opt_controller(alg::Optimizer, optim_factory::OptimizerFactory, params) |
| 18 | + return BlackBoxOptim.OptController( |
| 19 | + alg, |
| 20 | + problem(optim_factory), |
| 21 | + BlackBoxOptim.chain( |
| 22 | + BlackBoxOptim.DefaultParameters, |
| 23 | + OptController_DefaultParameters, |
| 24 | + params, |
| 25 | + ), |
| 26 | + ) |
| 27 | +end |
| 28 | + |
| 29 | +function check_population( |
| 30 | + factory::OptimizerFactory, |
| 31 | + popmatrix::BlackBoxOptim.PopulationMatrix, |
| 32 | +) |
| 33 | + ssp = factory |> problem |> search_space |
| 34 | + for i in 1:popsize(popmatrix) |
| 35 | + @assert popmatrix[:, i] ∈ ssp "Individual $i is out of space: $(popmatrix[:,i])" # fitness: $(fitness(population, i))" |
| 36 | + end |
| 37 | +end |
| 38 | + |
| 39 | +initial_search_space(factory::OptimizerFactory, id::Int) = search_space(factory.problem) |
| 40 | + |
| 41 | +function initial_population_matrix(factory::OptimizerFactory, id::Int) |
| 42 | + #@info "Standard initial_population_matrix()" |
| 43 | + ini_ss = initial_search_space(factory, id) |
| 44 | + if !isempty(factory.initial_population) |
| 45 | + numdims(factory.initial_population) == numdims(factory.problem) || throw( |
| 46 | + DimensionMismatch( |
| 47 | + "Dimensions of :Population ($(numdims(factory.initial_population))) " * |
| 48 | + "are different from the problem dimensions ($(numdims(factory.problem)))", |
| 49 | + ), |
| 50 | + ) |
| 51 | + res = factory.initial_population[ |
| 52 | + :, |
| 53 | + StatsBase.sample( |
| 54 | + 1:popsize(factory.initial_population), |
| 55 | + factory.population_size, |
| 56 | + ), |
| 57 | + ] |
| 58 | + else |
| 59 | + res = rand_individuals(ini_ss, factory.population_size, method = :latin_hypercube) |
| 60 | + end |
| 61 | + prj = RandomBound(ini_ss) |
| 62 | + if size(res, 2) > 1 |
| 63 | + apply!(prj, view(res, :, 1), SEM.start_fabin3(factory.problem.model)) |
| 64 | + end |
| 65 | + if size(res, 2) > 2 |
| 66 | + apply!(prj, view(res, :, 2), SEM.start_simple(factory.problem.model)) |
| 67 | + end |
| 68 | + return res |
| 69 | +end |
| 70 | + |
| 71 | +# convert individuals in the archive into population matrix |
| 72 | +population_matrix(archive::Any) = population_matrix!( |
| 73 | + Matrix{Float64}(undef, length(BlackBoxOptim.params(first(archive))), length(archive)), |
| 74 | + archive, |
| 75 | +) |
| 76 | + |
| 77 | +function population_matrix!(pop::AbstractMatrix{<:Real}, archive::Any) |
| 78 | + npars = length(BlackBoxOptim.params(first(archive))) |
| 79 | + size(pop, 1) == npars || throw( |
| 80 | + DimensionMismatch( |
| 81 | + "Matrix rows count ($(size(pop, 1))) doesn't match the number of problem dimensions ($(npars))", |
| 82 | + ), |
| 83 | + ) |
| 84 | + @inbounds for (i, indi) in enumerate(archive) |
| 85 | + (i <= size(pop, 2)) || break |
| 86 | + pop[:, i] .= BlackBoxOptim.params(indi) |
| 87 | + end |
| 88 | + if size(pop, 2) > length(archive) |
| 89 | + @warn "Matrix columns count ($(size(pop, 2))) is bigger than population size ($(length(archive))), last columns not set" |
| 90 | + end |
| 91 | + return pop |
| 92 | +end |
| 93 | + |
| 94 | +generate_embedder(factory::OptimizerFactory, id::Int, problem::OptimizationProblem) = |
| 95 | + RandomBound(search_space(problem)) |
| 96 | + |
| 97 | +abstract type DiffEvoFactory{P <: OptimizationProblem} <: OptimizerFactory{P} end |
| 98 | + |
| 99 | +generate_selector( |
| 100 | + factory::DiffEvoFactory, |
| 101 | + id::Int, |
| 102 | + problem::OptimizationProblem, |
| 103 | + population, |
| 104 | +) = RadiusLimitedSelector(get(factory.params, :selector_radius, popsize(population) ÷ 5)) |
| 105 | + |
| 106 | +function generate_modifier(factory::DiffEvoFactory, id::Int, problem::OptimizationProblem) |
| 107 | + ops = GeneticOperator[ |
| 108 | + MutationClock(UniformMutation(search_space(problem)), 1 / numdims(problem)), |
| 109 | + BlackBoxOptim.AdaptiveDiffEvoRandBin1( |
| 110 | + BlackBoxOptim.AdaptiveDiffEvoParameters( |
| 111 | + factory.params[:fdistr], |
| 112 | + factory.params[:crdistr], |
| 113 | + ), |
| 114 | + ), |
| 115 | + SimplexCrossover{3}(1.05), |
| 116 | + SimplexCrossover{2}(1.1), |
| 117 | + #SimulatedBinaryCrossover(0.05, 16.0), |
| 118 | + #SimulatedBinaryCrossover(0.05, 3.0), |
| 119 | + #SimulatedBinaryCrossover(0.1, 5.0), |
| 120 | + #SimulatedBinaryCrossover(0.2, 16.0), |
| 121 | + UnimodalNormalDistributionCrossover{2}( |
| 122 | + chain(BlackBoxOptim.UNDX_DefaultOptions, factory.params), |
| 123 | + ), |
| 124 | + UnimodalNormalDistributionCrossover{3}( |
| 125 | + chain(BlackBoxOptim.UNDX_DefaultOptions, factory.params), |
| 126 | + ), |
| 127 | + ParentCentricCrossover{2}(chain(BlackBoxOptim.PCX_DefaultOptions, factory.params)), |
| 128 | + ParentCentricCrossover{3}(chain(BlackBoxOptim.PCX_DefaultOptions, factory.params)), |
| 129 | + ] |
| 130 | + if problem isa SemModelBlackBoxOptimProblem |
| 131 | + push!( |
| 132 | + ops, |
| 133 | + AdamMutation(problem.model, chain(AdamMutation_DefaultOptions, factory.params)), |
| 134 | + ) |
| 135 | + end |
| 136 | + FAGeneticOperatorsMixture(ops) |
| 137 | +end |
| 138 | + |
| 139 | +function generate_optimizer( |
| 140 | + factory::DiffEvoFactory, |
| 141 | + id::Int, |
| 142 | + problem::OptimizationProblem, |
| 143 | + popmatrix, |
| 144 | +) |
| 145 | + population = FitPopulation(popmatrix, nafitness(fitness_scheme(problem))) |
| 146 | + BlackBoxOptim.DiffEvoOpt( |
| 147 | + "AdaptiveDE/rand/1/bin/gradient", |
| 148 | + population, |
| 149 | + generate_selector(factory, id, problem, population), |
| 150 | + generate_modifier(factory, id, problem), |
| 151 | + generate_embedder(factory, id, problem), |
| 152 | + ) |
| 153 | +end |
| 154 | + |
| 155 | +const Population_DefaultParameters = ParamsDict( |
| 156 | + :Population => BlackBoxOptim.PopulationMatrix(undef, 0, 0), |
| 157 | + :PopulationSize => 100, |
| 158 | +) |
| 159 | + |
| 160 | +const DE_DefaultParameters = chain( |
| 161 | + ParamsDict( |
| 162 | + :SelectorRadius => 0, |
| 163 | + :fdistr => |
| 164 | + BlackBoxOptim.BimodalCauchy(0.65, 0.1, 1.0, 0.1, clampBelow0 = false), |
| 165 | + :crdistr => |
| 166 | + BlackBoxOptim.BimodalCauchy(0.1, 0.1, 0.95, 0.1, clampBelow0 = false), |
| 167 | + ), |
| 168 | + Population_DefaultParameters, |
| 169 | +) |
| 170 | + |
| 171 | +struct DefaultDiffEvoFactory{P <: OptimizationProblem} <: DiffEvoFactory{P} |
| 172 | + problem::P |
| 173 | + initial_population::BlackBoxOptim.PopulationMatrix |
| 174 | + population_size::Int |
| 175 | + params::ParamsDictChain |
| 176 | +end |
| 177 | + |
| 178 | +DefaultDiffEvoFactory(problem::OptimizationProblem; kwargs...) = |
| 179 | + DefaultDiffEvoFactory(problem, BlackBoxOptim.kwargs2dict(kwargs)) |
| 180 | + |
| 181 | +function DefaultDiffEvoFactory(problem::OptimizationProblem, params::AbstractDict) |
| 182 | + params = chain(DE_DefaultParameters, params) |
| 183 | + DefaultDiffEvoFactory{typeof(problem)}( |
| 184 | + problem, |
| 185 | + params[:Population], |
| 186 | + params[:PopulationSize], |
| 187 | + params, |
| 188 | + ) |
| 189 | +end |
| 190 | + |
| 191 | +function BlackBoxOptim.bbsetup(factory::OptimizerFactory; kwargs...) |
| 192 | + popmatrix = initial_population_matrix(factory, 1) |
| 193 | + check_population(factory, popmatrix) |
| 194 | + alg = generate_optimizer(factory, 1, problem(factory), popmatrix) |
| 195 | + return generate_opt_controller(alg, factory, BlackBoxOptim.kwargs2dict(kwargs)) |
| 196 | +end |
0 commit comments