NLopt default arguments for objective function

The second question within two days which shows how rusty I’ve become not having used Julia on a daily basis for over a year… I’m trying to optimize a simple objective function using NLopt, which requires multiplying a matrix and a column vector (think of a simple regression framework with an n observations and m regressors, with a covariate matrix X sized (n x m) and a vector of coefficients sized (m x 1).

As NLopt only accepts one input argument, I’ve included the covariate matrix as a default argument in my obective function. Minimal (non-) working example below:

using NLopt

X = rand(100,10)          # 100x10 Array
w_0 = repmat([0.5],10)    # Float64[10]

X*w_0                     # works as expected: Float64[100]

function find_w(w, X=X)
  X*w
end

find_w(w_0)               # works as above: Float64[100]

to_min = Opt(:GN_MLSL, 10)
lower_bounds!(to_min, zeros(10))
upper_bounds!(to_min, ones(10))
min_objective!(to_min, find_w)
NLopt.optimize(to_min, w_0) # Fails: DimensionMismatch("Cannot multiply two vectors")

I’ve included print statements in my objective function which show that matin the find_w function is an empty Array{Float64,1} when the function is called within the NLopt optimization routine. That suggests to me that there is some sort of scope issue where the mat defined outside the function before optimization isn’t available to the function when called within the optimization. Any hints on how I need to define the objective function to get this to work are greatly appreciated!

(And as an aside: Am I reading the docs for the Optim package correctly in that it’s still not possible to do gradient-free multivariate boxed optimization with the package?)

Note that you can pass additional arguments just by using a closure. e.g.

min_objective!(to_min, (w,grad)->find_w(w,grad,X))

This is better than relying on globals. (This has got to be the most frequently asked question in NLopt — people just aren’t used to closures, even in languages like Julia where they are a core feature.)

Also, if you read the NLopt docs, you’ll notice that your objective function always needs to take two arguments, the second argument being used to output the gradient in-place. This is true even for derivative-free optimization algorithms (since they use the same API), which pass an empty array for the gradient. That’s also why you are getting the empty array in your find_w example, because that is the (empty) gradient vector overriding your default arguments.

1 Like

Of course, the gradient! Doubly embarrassing question for me to ask as this isn’t just the most frequently asked question for NLopt as you say, I’m pretty sure that this is a question I asked (and you answered) back on the old Google forum when I first used NLopt about four years ago…

An interesting point on the closures as well, I was indeed unaware of this and it looks really useful, thanks!