Rationale for black-box optim
Since Julia have powerful auto-differentiation library (such as Zygote.jl
) and useful Optimization (convex or not) packages such as JuMP.jl
, one may think a black-box optimization is not useful in almost any case. However, even within the pure-Julia ecosystem, one might find the function at hand having noisy/spiky differential, or, takes too long to compute not mentioning the differentials.
Another place a black-box optimization may come handy is when dealing with external programs, especially those closed source once or legacy (read: fossil) softwares.
A short example
Say we have some compiled C
program with the following function:
// ./test.c
#include <math.h>
double parab(double x){
return pow((3+x), 2);
}
we can compile a shared library:
gcc -c -Wall -fPIC test.c -o test.o
gcc -shared -fPIC -o test.so test.o
# gives us a ./test.so
We can use @ccall
to access this function:
julia> @ccall "./test".parab(2.0::Float64)::Float64
25.0
Now, we wrap this into a function:[1]
julia> parab_c(x) = @ccall "./test".parab(x[1]::Float64)::Float64
parab_c (generic function with 1 method)
then we can black-box optimize it!
using BlackBoxOptim
julia> res = bboptimize(parab_c; SearchRange=(-5.0, 5.0), NumDimensions=1)
...
julia> best_candidate(res)
1-element Array{Float64,1}:
-3.0
In fact, we would realize that we don't care how the external program is invoked, as long as the returned 'stuff' is stable; you might as well just parse the stdout
of some weird external program into numbers and feed it into bboptimize
.
[1] | BlackBoxOptim.jl expects the function to take an Array even though your function may be a scalar one. |