Skip to content

Commit 2fb6475

Browse files
committed
Added basic NOMAD integration
Added basic NOMAD integration with tests and documentation
1 parent 129113b commit 2fb6475

File tree

6 files changed

+129
-2
lines changed

6 files changed

+129
-2
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ makedocs(
3434
"MathOptInterface.jl" => "optimization_packages/mathoptinterface.md",
3535
"MultistartOptimization.jl" => "optimization_packages/multistartoptimization.md",
3636
"Metaheuristics.jl" => "optimization_packages/metaheuristics.md",
37+
"NOMAD.jl" => "optimization_packages/nomad.md",
3738
"NLopt.jl" => "optimization_packages/nlopt.md",
3839
"Nonconvex.jl" => "optimization_packages/nonconvex.md",
3940
"Optim.jl" => "optimization_packages/optim.md",
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# NOMAD.jl
2+
[`NOMAD`](https://github.com/bbopt/NOMAD.jl) is Julia package interfacing to NOMAD,which is a C++ implementation of the Mesh Adaptive Direct Search algorithm (MADS), designed for difficult blackbox optimization problems. These problems occur when the functions defining the objective and constraints are the result of costly computer simulations. [`NOMAD.jl documentation`](https://bbopt.github.io/NOMAD.jl/stable/)
3+
4+
The NOMAD algorithm is called by `NOMADOpt()`
5+
6+
## Global Optimizer
7+
### Without Constraint Equations
8+
9+
The method in [`NOMAD`](https://github.com/bbopt/NOMAD.jl) is performing global optimization on problems both with and without
10+
constraint equations. Currently however, linear and nonlinear constraints defined in `GalacticOPtim` are not passed.
11+
12+
NOMAD works both with and without lower and upper boxconstraints set by `lb` and `ub` in the `OptimizationProblem`.
13+
14+
15+
The Rosenbrock function can optimized using the `NOMADOpt()` with and without boxcontraints as follows:
16+
17+
```julia
18+
rosenbrock(x, p) = (p[1] - x[1])^2 + p[2] * (x[2] - x[1]^2)^2
19+
x0 = zeros(2)
20+
p = [1.0, 100.0]
21+
f = OptimizationFunction(rosenbrock)
22+
23+
prob = OptimizationProblem(f, x0, _p)
24+
sol = GalacticOptim.solve(prob,NOMADOpt())
25+
26+
prob = OptimizationProblem(f, x0, _p, lb = [-1.0,-1.0], ub = [1.5,1.5])
27+
sol = GalacticOptim.solve(prob,NOMADOpt())
28+
```

src/GalacticOptim.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ function __init__()
4343
end
4444
end
4545
@require Metaheuristics="bcdb8e00-2c21-11e9-3065-2b553b22f898" include("solve/metaheuristics.jl")
46+
@require NOMAD="02130f1c-4665-5b79-af82-ff1385104aa0" include("solve/nomad.jl")
4647

4748
# AD backends
4849
@require FiniteDiff="6a86dc24-6348-571c-b903-95158fe2bd41" include("function/finitediff.jl")

src/solve/nomad.jl

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
export NOMADOpt
2+
struct NOMADOpt end
3+
4+
5+
function __map_optimizer_args(prob::OptimizationProblem, opt::NOMAD.NomadProblem;
6+
cb=nothing,
7+
maxiters::Union{Number, Nothing}=nothing,
8+
maxtime::Union{Number, Nothing}=nothing,
9+
abstol::Union{Number, Nothing}=nothing,
10+
reltol::Union{Number, Nothing}=nothing,
11+
kwargs...)
12+
13+
for j in kwargs
14+
setproperty!(opt.options, j.first, j.second)
15+
end
16+
17+
if !isnothing(maxiters)
18+
opt.options.max_bb_eval=maxiters
19+
end
20+
21+
if !isnothing(maxtime)
22+
opt.options.max_time=maxtime
23+
end
24+
25+
if !isnothing(reltol)
26+
@warn "common reltol is currently not used by $(opt)"
27+
end
28+
29+
if !isnothing(abstol)
30+
@warn "common abstol is currently not used by $(opt)"
31+
end
32+
33+
return nothing
34+
end
35+
36+
function __solve(prob::OptimizationProblem, opt::NOMADOpt;
37+
maxiters::Union{Number, Nothing} = nothing,
38+
maxtime::Union{Number, Nothing} = nothing,
39+
abstol::Union{Number, Nothing}=nothing,
40+
reltol::Union{Number, Nothing}=nothing,
41+
progress = false,
42+
kwargs...)
43+
44+
local x
45+
46+
maxiters = _check_and_convert_maxiters(maxiters)
47+
maxtime = _check_and_convert_maxtime(maxtime)
48+
49+
50+
_loss = function(θ)
51+
x = prob.f(θ, prob.p)
52+
return first(x)
53+
end
54+
55+
function bb(x)
56+
bb_outputs = [_loss(x)]
57+
success = true
58+
count_eval = true
59+
return (success, count_eval, bb_outputs)
60+
end
61+
62+
if !isnothing(prob.lcons) | !isnothing(prob.ucons)
63+
@warn "Linear and nonlinear constraints defined in OptimizationProblem are currently not used by $(opt)"
64+
end
65+
66+
bounds=(;)
67+
if !isnothing(prob.lb)
68+
bounds = (; bounds..., lower_bound=prob.lb)
69+
end
70+
71+
if !isnothing(prob.ub)
72+
bounds = (; bounds..., upper_bound=prob.ub)
73+
end
74+
75+
opt_setup = NOMAD.NomadProblem(length(prob.u0), 1, ["OBJ"], bb; bounds...)
76+
77+
_map_optimizer_args(prob, opt_setup, maxiters=maxiters, maxtime=maxtime,abstol=abstol, reltol=reltol; kwargs...)
78+
79+
t0 = time()
80+
opt_res = NOMAD.solve(opt_setup, prob.u0)
81+
t1 = time()
82+
83+
SciMLBase.build_solution(prob, opt, opt_res.x_best_feas, first(opt_res.bbo_best_feas); original=opt_res)
84+
end

test/Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Metaheuristics = "bcdb8e00-2c21-11e9-3065-2b553b22f898"
1414
ModelingToolkit = "961ee093-0014-501f-94e3-6117800e7a78"
1515
MultistartOptimization = "3933049c-43be-478e-a8bb-6e0f7fd53575"
1616
NLopt = "76087f3c-5699-56af-9a33-bf431cd00edd"
17+
NOMAD = "02130f1c-4665-5b79-af82-ff1385104aa0"
1718
Nonconvex = "01bcebdf-4d21-426d-b5c4-6132c1619978"
1819
NonconvexBayesian = "fb352abc-de7b-48de-9ebd-665b54b5d9b3"
1920
NonconvexIpopt = "bf347577-a06d-49ad-a669-8c0e005493b8"
@@ -49,6 +50,7 @@ Metaheuristics = ">=3.0.2"
4950
ModelingToolkit = ">= 6.4.7"
5051
MultistartOptimization = ">= 0.1.2"
5152
NLopt = ">= 0.6"
53+
NOMAD = ">= 2.1"
5254
Nonconvex = ">= 1.0"
5355
NonconvexBayesian = ">= 0.1"
5456
NonconvexIpopt = ">= 0.1"
@@ -65,4 +67,4 @@ ReverseDiff = ">= 1.9.0"
6567
SafeTestsets = ">= 0.0.1"
6668
Tracker = ">= 0.2"
6769
Zygote = ">= 0.5"
68-
julia = "1.5"
70+
julia = "1.5"

test/rosenbrock.jl

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ end
173173
# sol = solve(prob, QuadDirect(); splits = ([-0.5, 0.0, 0.5],[-0.5, 0.0, 0.5]))
174174
# @test 10*sol.minimum < l1
175175

176-
@testset "Evolutionary, BlackBoxOptim, Metaheuristics, Nonconvex" begin
176+
@testset "Evolutionary, BlackBoxOptim, Metaheuristics, Nonconvex, NOMAD" begin
177177
optprob = OptimizationFunction(rosenbrock, GalacticOptim.AutoZygote())
178178
using Evolutionary
179179
prob = GalacticOptim.OptimizationProblem(optprob, x0, _p)
@@ -426,4 +426,15 @@ end
426426

427427
sol = solve(prob, BayesOptAlg(NLoptAlg(:LN_NELDERMEAD)), sub_options=(;maxeval=100))
428428
@test 10*sol.minimum < l1
429+
430+
using NOMAD
431+
f = OptimizationFunction(rosenbrock)
432+
433+
prob = OptimizationProblem(f, x0, _p)
434+
sol = GalacticOptim.solve(prob,NOMADOpt())
435+
@test 10*sol.minimum < l1
436+
437+
prob = OptimizationProblem(f, x0, _p; lb = [-1.0,-1.0], ub = [1.5,1.5])
438+
sol = GalacticOptim.solve(prob,NOMADOpt())
439+
@test 10*sol.minimum < l1
429440
end

0 commit comments

Comments
 (0)