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

Updating solver __solve function for MOO #787

Merged
merged 18 commits into from
Aug 19, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions lib/OptimizationBBO/src/OptimizationBBO.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module OptimizationBBO
using Reexport
import Optimization
import BlackBoxOptim, Optimization.SciMLBase
import Optimization.SciMLBase: MultiObjectiveOptimizationFunction

abstract type BBO end

Expand All @@ -15,6 +16,11 @@ for j in string.(BlackBoxOptim.SingleObjectiveMethodNames)
eval(Meta.parse("export BBO_" * j))
end

Base.@kwdef struct BBO_borg_moea <: BBO
method = :borg_moea
end
export BBO_borg_moea

function decompose_trace(opt::BlackBoxOptim.OptRunController, progress)
if progress
maxiters = opt.max_steps
Expand Down Expand Up @@ -142,17 +148,32 @@ function SciMLBase.__solve(cache::Optimization.OptimizationCache{
maxtime = Optimization._check_and_convert_maxtime(cache.solver_args.maxtime)

_loss = function (θ)
if cache.callback === Optimization.DEFAULT_CALLBACK &&
cache.data === Optimization.DEFAULT_DATA
return first(cache.f(θ, cache.p))
elseif cache.callback === Optimization.DEFAULT_CALLBACK
return first(cache.f(θ, cache.p, cur...))
elseif cache.data !== Optimization.DEFAULT_DATA
x = cache.f(θ, cache.p)
return first(x)
if isa(cache.f, MultiObjectiveOptimizationFunction)
if cache.callback === Optimization.DEFAULT_CALLBACK &&
cache.data === Optimization.DEFAULT_DATA
return cache.f(θ, cache.p)
elseif cache.callback === Optimization.DEFAULT_CALLBACK
return cache.f(θ, cache.p, cur...)
elseif cache.data !== Optimization.DEFAULT_DATA
x = cache.f(θ, cache.p)
return x
else
x = cache.f(θ, cache.p, cur...)
return first(x)
end
else
x = cache.f(θ, cache.p, cur...)
return first(x)
if cache.callback === Optimization.DEFAULT_CALLBACK &&
cache.data === Optimization.DEFAULT_DATA
return first(cache.f(θ, cache.p))
elseif cache.callback === Optimization.DEFAULT_CALLBACK
return first(cache.f(θ, cache.p, cur...))
elseif cache.data !== Optimization.DEFAULT_DATA
x = cache.f(θ, cache.p)
return first(x)
else
x = cache.f(θ, cache.p, cur...)
return first(x)
end
end
end

Expand Down
63 changes: 62 additions & 1 deletion lib/OptimizationBBO/test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using OptimizationBBO, Optimization
using OptimizationBBO, Optimization, BlackBoxOptim
using SciMLBase: MultiObjectiveOptimizationFunction
using Test
Vaibhavdixit02 marked this conversation as resolved.
Show resolved Hide resolved

@testset "OptimizationBBO.jl" begin
Expand Down Expand Up @@ -46,4 +47,64 @@ using Test
progress = true,
maxtime = 5)
end

# Define the initial guess and bounds
u0 = [0.25, 0.25]
lb = [0.0, 0.0]
ub = [2.0, 2.0]

# Define the optimizer
opt = OptimizationBBO.BBO_borg_moea()

@testset "Multi-Objective Optimization Tests" begin

# Test 1: Sphere and Rastrigin Functions
@testset "Sphere and Rastrigin Functions" begin
function multi_obj_func_1(x, p)
f1 = sum(x .^ 2) # Sphere function
f2 = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10) # Rastrigin function
return (f1, f2)
end

mof_1 = MultiObjectiveOptimizationFunction(multi_obj_func_1)
prob_1 = Optimization.OptimizationProblem(mof_1, u0; lb=lb, ub=ub)
sol_1 = solve(prob_1, opt, NumDimensions=2, FitnessScheme=ParetoFitnessScheme{2}(is_minimizing=true))

@test sol_1 ≠ nothing
println("Solution for Sphere and Rastrigin: ", sol_1)
end

# Test 2: Rosenbrock and Ackley Functions
@testset "Rosenbrock and Ackley Functions" begin
function multi_obj_func_2(x, p)
f1 = (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2 # Rosenbrock function
f2 = -20.0 * exp(-0.2 * sqrt(0.5 * (x[1]^2 + x[2]^2))) - exp(0.5 * (cos(2π * x[1]) + cos(2π * x[2]))) + exp(1) + 20.0 # Ackley function
return (f1, f2)
end

mof_2 = MultiObjectiveOptimizationFunction(multi_obj_func_2)
prob_2 = Optimization.OptimizationProblem(mof_2, u0; lb=lb, ub=ub)
sol_2 = solve(prob_2, opt, NumDimensions=2, FitnessScheme=ParetoFitnessScheme{2}(is_minimizing=true))

@test sol_2 ≠ nothing
println("Solution for Rosenbrock and Ackley: ", sol_2)
end

# Test 3: ZDT1 Function
@testset "ZDT1 Function" begin
function multi_obj_func_3(x, p)
f1 = x[1]
g = 1 + 9 * sum(x[2:end]) / (length(x) - 1)
f2 = g * (1 - sqrt(f1 / g))
return (f1, f2)
end

mof_3 = MultiObjectiveOptimizationFunction(multi_obj_func_3)
prob_3 = Optimization.OptimizationProblem(mof_3, u0; lb=lb, ub=ub)
sol_3 = solve(prob_3, opt, NumDimensions=2, FitnessScheme=ParetoFitnessScheme{2}(is_minimizing=true))

@test sol_3 ≠ nothing
println("Solution for ZDT1: ", sol_3)
end
end
end
10 changes: 8 additions & 2 deletions lib/OptimizationEvolutionary/src/OptimizationEvolutionary.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ SciMLBase.allowsbounds(opt::Evolutionary.AbstractOptimizer) = true
SciMLBase.allowsconstraints(opt::Evolutionary.AbstractOptimizer) = true
SciMLBase.supports_opt_cache_interface(opt::Evolutionary.AbstractOptimizer) = true
SciMLBase.requiresgradient(opt::Evolutionary.AbstractOptimizer) = false
SciMLBase.requiresgradient(opt::Evolutionary.NSGA2) = false
SciMLBase.requireshessian(opt::Evolutionary.AbstractOptimizer) = false
SciMLBase.requiresconsjac(opt::Evolutionary.AbstractOptimizer) = false
SciMLBase.requiresconshess(opt::Evolutionary.AbstractOptimizer) = false
Expand Down Expand Up @@ -125,8 +126,13 @@ function SciMLBase.__solve(cache::OptimizationCache{
f = cache.f

_loss = function (θ)
x = f(θ, cache.p, cur...)
return first(x)
if isa(f, MultiObjectiveOptimizationFunction)
x = f(θ, cache.p, cur...)
return x
else
x = f(θ, cache.p, cur...)
return first(x)
end
end

opt_args = __map_optimizer_args(cache, cache.opt; callback = _cb, cache.solver_args...,
Expand Down
90 changes: 90 additions & 0 deletions lib/OptimizationEvolutionary/test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using OptimizationEvolutionary, Optimization, Random
using SciMLBase: MultiObjectiveOptimizationFunction
Vaibhavdixit02 marked this conversation as resolved.
Show resolved Hide resolved
using NaNMath
using Test

Random.seed!(1234)
Expand Down Expand Up @@ -56,4 +58,92 @@ Random.seed!(1234)
# Make sure that both the user's trace record value, as well as `curr_u` are stored in the trace.
@test haskey(sol.original.trace[end].metadata, "TESTVAL") &&
haskey(sol.original.trace[end].metadata, "curr_u")

# Test Suite for Different Multi-Objective Functions
function test_multi_objective(func, initial_guess)
# Define the gradient function using ForwardDiff
function gradient_multi_objective(x, p=nothing)
ForwardDiff.jacobian(func, x)
end

# Create an instance of MultiObjectiveOptimizationFunction
obj_func = MultiObjectiveOptimizationFunction(func, jac=gradient_multi_objective)

# Set up the evolutionary algorithm (e.g., NSGA2)
algorithm = OptimizationEvolutionary.NSGA2()

# Define the optimization problem
problem = OptimizationProblem(obj_func, initial_guess)

# Solve the optimization problem
result = solve(problem, algorithm)

return result
end

@testset "Multi-Objective Optimization Tests" begin

# Test 1: Sphere and Rastrigin Functions
@testset "Sphere and Rastrigin Functions" begin
function multi_objective_1(x, p=nothing)::Vector{Float64}
f1 = sum(x .^ 2) # Sphere function
f2 = sum(x .^ 2 .- 10 .* cos.(2π .* x) .+ 10) # Rastrigin function
return [f1, f2]
end
result = test_multi_objective(multi_objective_1, [0.25, 0.25])
@test result ≠ nothing
println("Solution for Sphere and Rastrigin: ", result)
end

# Test 2: Rosenbrock and Ackley Functions
@testset "Rosenbrock and Ackley Functions" begin
function multi_objective_2(x, p=nothing)::Vector{Float64}
f1 = (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2 # Rosenbrock function
f2 = -20.0 * exp(-0.2 * sqrt(0.5 * (x[1]^2 + x[2]^2))) - exp(0.5 * (cos(2π * x[1]) + cos(2π * x[2]))) + exp(1) + 20.0 # Ackley function
return [f1, f2]
end
result = test_multi_objective(multi_objective_2, [1.0, 1.0])
@test result ≠ nothing
println("Solution for Rosenbrock and Ackley: ", result)
end

# Test 3: ZDT1 Function
@testset "ZDT1 Function" begin
function multi_objective_3(x, p=nothing)::Vector{Float64}
f1 = x[1]
g = 1 + 9 * sum(x[2:end]) / (length(x) - 1)
sqrt_arg = f1 / g
f2 = g * (1 - (sqrt_arg >= 0 ? sqrt(sqrt_arg) : NaN))
return [f1, f2]
end
result = test_multi_objective(multi_objective_3, [0.5, 0.5])
@test result ≠ nothing
println("Solution for ZDT1: ", result)
end

# Test 4: DTLZ2 Function
@testset "DTLZ2 Function" begin
function multi_objective_4(x, p=nothing)::Vector{Float64}
f1 = (1 + sum(x[2:end] .^ 2)) * cos(x[1] * π / 2)
f2 = (1 + sum(x[2:end] .^ 2)) * sin(x[1] * π / 2)
return [f1, f2]
end
result = test_multi_objective(multi_objective_4, [0.5, 0.5])
@test result ≠ nothing
println("Solution for DTLZ2: ", result)
end

# Test 5: Schaffer Function N.2
@testset "Schaffer Function N.2" begin
function multi_objective_5(x, p=nothing)::Vector{Float64}
f1 = x[1]^2
f2 = (x[1] - 2)^2
return [f1, f2]
end
result = test_multi_objective(multi_objective_5, [2.0])
@test result ≠ nothing
println("Solution for Schaffer N.2: ", result)
end

end
end
Loading