I have a nonlinear optimization problem that I’ve tried solving with scipy. For small scale it (the ‘TNC’ option in scipy minimize, specifically) works well, but it’s too slow when I get to thousands of variables. I’m learning Julia to see if I can find something better here.
Here is an example of a small scale version of the problem:
(Using ComponentArrays for convenience)
k_unr_out = [ 0.,  3.,  1.,  5.,  2.,  4.,  5.,  0.,  0.,  2.,  0.,  4.,  5.,
         6.,  1., 22.,  0.,  1., 19.,  0.,  0.,  7.,  0.,  4.,  0.,  6.]
k_unr_in = [ 7.,  4.,  9.,  3.,  2.,  3.,  4., 11.,  4.,  4.,  5.,  4.,  3.,
         3.,  3.,  0.,  2.,  6.,  0.,  3.,  0.,  1.,  8.,  2.,  3.,  3.]
k_recip = [ 1.,  8.,  2.,  8.,  3.,  7.,  6.,  1.,  1.,  7.,  4.,  5.,  4.,
         2.,  9.,  3.,  2.,  6.,  6.,  2., 25.,  6.,  4.,  3.,  3.,  6.]
N = length(k_unr_out)
v0 = ComponentArray(x=repeat([0.1], N), y=repeat([0.1], N), z=repeat([0.1], N))
function neg_llhood(v, k_unr_out, k_unr_in, k_recip)
    llhood = sum(k_unr_out .* log.(v.x))
    llhood += sum(k_unr_in .* log.(v.y))
    llhood += sum(k_recip .* log.(v.z))
        
    xy = v.x .* v.y'
    zz = v.z .* v.z'
    Q = log.(1 .+ xy + xy' + zz)
    
    llhood -= sum(tril(Q, -1))
    
    -llhood
end
f = v -> neg_llhood(v, k_unr_out, k_unr_in, k_recip)
Since logarithms are taken for everything, my only constraints are x_i, y_i, z_i > 0 for all i. I’d like to use Optim to solve the above but I’m not sure exactly how to do that.
In scipy I was using JAX to do auto differentiation so I’m trying to do that here as well (I could compute the gradient analytically if I need to tho). For box constraints the docs for Optim give this example,
lower = [1.25, -2.1]
upper = [Inf, Inf]
initial_x = [2.0, 2.0]
inner_optimizer = GradientDescent()
results = optimize(f, g!, lower, upper, initial_x, Fminbox(inner_optimizer))
I’m confused by the docs. What is Fminbox doing? What is an inner optimizer? How do I specify that I want to use AD?
I tried
lower = repeat([floatmin()], 3*N)
upper = repeat([Inf], 3*N)
inner_optimizer = GradientDescent()
results = optimize(f, lower, upper, v0, Fminbox(inner_optimizer); autodiff=:forward)
And I get
MethodError: no method matching ldiv!(::ComponentVector{Float64}, ::Optim.InverseDiagonal, ::ComponentVector{Float64})
Closest candidates are:
...
I’m not sure if JuMP + a solver is preferred for my given problem. I gave it a try
model = Model(Ipopt.Optimizer)
@variable(model, x[1:N] >= floatmin(), start=0.1)
@variable(model, y[1:N] >= floatmin(), start=0.1)
@variable(model, z[1:N] >= floatmin(), start=0.1)
@NLobjective(model, Max,
    sum(k_unr_out[i] * log(x[i]) + k_unr_in[i] * log(y[i]) + k_recip[i] * log(z[i]) for i in 1:N)
    - sum(log(1 + x[i] * y[j] + x[j] * y[i] + z[i] * z[j]) for i in 1:N for j in i+1:N))
optimize!(model)
This causes my Jupyter kernel to crash.
I’m a bit lost in how to solve this problem most efficiently. Any help appreciated.