I am using Optim.jl with an objective that is computed by multipliying, transposing and inverting some matrices (maximum size of about 500x500). However, I have only a few parameters (maximum 200), each one having a fixed position in those matrices.
Until now, I used functions to define the matrices every time the objective function is called, but this leads to huge performance drawbacks as memory has to be allocated every time. Therefore, I would like to define the matrices in a struct outside of the objective function and only change the entries to the values of the parameters each time the objective is called.
This leads to the problem that the matrices are of Type Array{Float64}, while the parameters are Dual Numbers.
This mwe reproduces the Error:
using Optim, ForwardDiff
mutable struct MyStruct{T}
a::T
end
A = [0.0 0
0 0]
teststruct = MyStruct(A)
function prepare_struct(par, mystruct)
mystruct.a[2,2] = par[1]
return mystruct
end
function objective(mystruct)
mystruct.a[2,2]^2
end
function wrap(par, mystruct)
mystruct = prepare_struct(par, mystruct)
return objective(mystruct)
end
optimize(par -> wrap(par, teststruct),
[5.0],
LBFGS(),
autodiff = :forward)
ERROR: MethodError: no method matching Float64(::ForwardDiff.Dual{ForwardDiff.Tag{var"#11#12",Float64},Float64,1})
One possible remedy would be converting the matrices before changing the parameters:
function myconvert(par, mystruct)
T = eltype(par)
new_array = convert(Array{T}, mystruct.a)
mystruct_conv = MyStruct(new_array)
return mystruct_conv
end
function new_wrap(par, mystruct)
mystruct = myconvert(par, mystruct)
mystruct = prepare_struct(par, mystruct)
return objective(mystruct)
end
optimize(par -> new_wrap(par, teststruct),
[5.0],
LBFGS(),
autodiff = :forward)
Is converting a good/the most performant solution in this case?