Skip to content

Commit ebe2aec

Browse files
MOO tests and code updates
1 parent 66919e4 commit ebe2aec

File tree

3 files changed

+111
-5
lines changed

3 files changed

+111
-5
lines changed

lib/OptimizationBBO/src/OptimizationBBO.jl

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,25 @@ function __map_optimizer_args(prob::Optimization.OptimizationCache, opt::BBO;
5353
if !isnothing(reltol)
5454
@warn "common reltol is currently not used by $(opt)"
5555
end
56-
mapped_args = (; kwargs...)
57-
mapped_args = (; mapped_args..., Method = opt.method,
58-
SearchRange = [(prob.lb[i], prob.ub[i]) for i in 1:length(prob.lb)])
56+
57+
# Determine number of objectives for multi-objective problems
58+
if isa(prob.f, MultiObjectiveOptimizationFunction)
59+
num_objectives = length(prob.f.cost_prototype)
60+
mapped_args = (; kwargs...)
61+
mapped_args = (; mapped_args..., Method = opt.method,
62+
SearchRange = [(prob.lb[i], prob.ub[i]) for i in 1:length(prob.lb)],
63+
NumDimensions = length(prob.lb),
64+
NumObjectives = num_objectives)
65+
# FitnessScheme should be in opt, not the function
66+
if hasproperty(opt, :FitnessScheme)
67+
mapped_args = (; mapped_args..., FitnessScheme = opt.FitnessScheme)
68+
end
69+
else
70+
mapped_args = (; kwargs...)
71+
mapped_args = (; mapped_args..., Method = opt.method,
72+
SearchRange = [(prob.lb[i], prob.ub[i]) for i in 1:length(prob.lb)],
73+
NumDimensions = length(prob.lb))
74+
end
5975

6076
if !isnothing(callback)
6177
mapped_args = (; mapped_args..., CallbackFunction = callback,
@@ -144,8 +160,16 @@ function SciMLBase.__solve(cache::Optimization.OptimizationCache{
144160
maxiters = Optimization._check_and_convert_maxiters(cache.solver_args.maxiters)
145161
maxtime = Optimization._check_and_convert_maxtime(cache.solver_args.maxtime)
146162

147-
_loss = function (θ)
148-
cache.f(θ, cache.p)
163+
164+
# Multi-objective: use out-of-place or in-place as appropriate
165+
if isa(cache.f, MultiObjectiveOptimizationFunction)
166+
if cache.f.iip
167+
_loss = θ -> (cost = similar(cache.f.cost_prototype); cache.f.f(cost, θ, cache.p); cost)
168+
else
169+
_loss = θ -> cache.f.f(θ, cache.p)
170+
end
171+
else
172+
_loss = θ -> cache.f(θ, cache.p)
149173
end
150174

151175
opt_args = __map_optimizer_args(cache, cache.opt;

lib/OptimizationBBO/test/runtests.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,50 @@ using Test
3434
push!(loss_history, fitness)
3535
return false
3636
end
37+
38+
@testset "In-place Multi-Objective Optimization" begin
39+
function inplace_multi_obj!(cost, x, p)
40+
cost[1] = sum(x .^ 2)
41+
cost[2] = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10)
42+
return nothing
43+
end
44+
u0 = [0.25, 0.25]
45+
lb = [0.0, 0.0]
46+
ub = [2.0, 2.0]
47+
cost_prototype = zeros(2)
48+
mof_inplace = MultiObjectiveOptimizationFunction(inplace_multi_obj!; cost_prototype=cost_prototype)
49+
prob_inplace = Optimization.OptimizationProblem(mof_inplace, u0; lb=lb, ub=ub)
50+
sol_inplace = solve(prob_inplace, opt, NumDimensions=2, FitnessScheme=ParetoFitnessScheme{2}(is_minimizing=true))
51+
@test sol_inplace nothing
52+
@test length(sol_inplace.objective) == 2
53+
@test sol_inplace.objective[1] 6.9905986e-18 atol=1e-3
54+
@test sol_inplace.objective[2] 1.7763568e-15 atol=1e-3
55+
end
56+
57+
@testset "Custom coalesce for Multi-Objective" begin
58+
function multi_obj_tuple(x, p)
59+
f1 = sum(x .^ 2)
60+
f2 = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10)
61+
return (f1, f2)
62+
end
63+
coalesce_sum(cost, x, p) = sum(cost)
64+
mof_coalesce = MultiObjectiveOptimizationFunction(multi_obj_tuple; coalesce=coalesce_sum)
65+
prob_coalesce = Optimization.OptimizationProblem(mof_coalesce, u0; lb=lb, ub=ub)
66+
sol_coalesce = solve(prob_coalesce, opt, NumDimensions=2, FitnessScheme=ParetoFitnessScheme{2}(is_minimizing=true))
67+
@test sol_coalesce nothing
68+
@test sol_coalesce.objective[1] 6.9905986e-18 atol=1e-3
69+
@test sol_coalesce.objective[2] 1.7763568e-15 atol=1e-3
70+
@test mof_coalesce.coalesce([1.0, 2.0], [0.0, 0.0], nothing) == 3.0
71+
end
72+
73+
@testset "Error if in-place MultiObjectiveOptimizationFunction without cost_prototype" begin
74+
function inplace_multi_obj_err!(cost, x, p)
75+
cost[1] = sum(x .^ 2)
76+
cost[2] = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10)
77+
return nothing
78+
end
79+
@test_throws ArgumentError MultiObjectiveOptimizationFunction(inplace_multi_obj_err!)
80+
end
3781
sol = solve(prob, BBO_adaptive_de_rand_1_bin_radiuslimited(), callback = cb)
3882
# println(fitness_progress_history)
3983
@test !isempty(fitness_progress_history)

lib/OptimizationEvolutionary/test/runtests.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,44 @@ Random.seed!(1234)
4040
if state.iter % 10 == 0
4141
println(state.u)
4242
end
43+
44+
@testset "In-place Multi-Objective Optimization" begin
45+
function inplace_multi_obj!(cost, x, p)
46+
cost[1] = sum(x .^ 2)
47+
cost[2] = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10)
48+
return nothing
49+
end
50+
u0 = [0.25, 0.25]
51+
cost_prototype = zeros(2)
52+
mof_inplace = MultiObjectiveOptimizationFunction(inplace_multi_obj!; cost_prototype=cost_prototype)
53+
prob_inplace = OptimizationProblem(mof_inplace, u0)
54+
sol_inplace = solve(prob_inplace, NSGA2())
55+
@test sol_inplace nothing
56+
@test length(sol_inplace.objective) == 2
57+
end
58+
59+
@testset "Custom coalesce for Multi-Objective" begin
60+
function multi_obj_tuple(x, p)
61+
f1 = sum(x .^ 2)
62+
f2 = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10)
63+
return (f1, f2)
64+
end
65+
coalesce_sum(cost, x, p) = sum(cost)
66+
mof_coalesce = MultiObjectiveOptimizationFunction(multi_obj_tuple; coalesce=coalesce_sum)
67+
prob_coalesce = OptimizationProblem(mof_coalesce, u0)
68+
sol_coalesce = solve(prob_coalesce, NSGA2())
69+
@test sol_coalesce nothing
70+
@test mof_coalesce.coalesce([1.0, 2.0], [0.0, 0.0], nothing) == 3.0
71+
end
72+
73+
@testset "Error if in-place MultiObjectiveOptimizationFunction without cost_prototype" begin
74+
function inplace_multi_obj_err!(cost, x, p)
75+
cost[1] = sum(x .^ 2)
76+
cost[2] = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10)
77+
return nothing
78+
end
79+
@test_throws ArgumentError MultiObjectiveOptimizationFunction(inplace_multi_obj_err!)
80+
end
4381
return false
4482
end
4583
solve(prob, CMAES= 40, λ = 100), callback = cb, maxiters = 100)

0 commit comments

Comments
 (0)