Combining `Optim` and `FieldVector` from `StaticArrays`

I’m getting a MethodError in LBFGS when trying to use a custom mutable struct that inherits from FieldVector for an optimization problem.

using StaticArrays
using Optimization, OptimizationOptimJL

mutable struct Point{T} <: FieldVector{2, T}
    x::T
    y::T
end

function rosenbrock(v, p)
    (p[1] - v[1])^2 + p[2] * (v[2] - v[1]^2)^2
end

v₀ = Point(0., 0.)
p = (1., 100.)

f = OptimizationFunction(rosenbrock, Optimization.AutoForwardDiff())
prob = OptimizationProblem(f, v₀, p)
sol = solve(prob, LBFGS())  # ERROR: MethodError: no method matching Optim.LBFGSState(::Point{…}, ...)

My first attept was to write a new rosenbrock method as

function rosenbrock(v::Point, p)
    rosenbrock(SVector(v.x, v.y), p)
end

but of course this still fails. Is there a good way to get this to work?

1 Like

Hi! Can you share the complete stack trace?

2 Likes

I get

julia> sol = solve(prob, LBFGS())  # ERROR: MethodError: no method matching Optim.LBFGSState(::Point{…}, ...)
ERROR: MethodError: no method matching Optim.LBFGSState(::Point{…}, ::Point{…}, ::Point{…}, ::Vector{…}, ::Vector{…}, ::Vector{…}, ::Point{…}, ::Point{…}, ::Point{…}, ::Float64, ::MVector{…}, ::Vector{…}, ::Int64, ::Point{…}, ::MVector{…}, ::Float64)
The type `Optim.LBFGSState` exists, but no method is defined for this combination of argument types when trying to construct it.

Closest candidates are:
  Optim.LBFGSState(::Tx, ::Tx, ::G, ::Vector{T}, ::Tdx, ::Tdg, ::Tx, ::Tx, ::Tx, ::T, ::Any, ::Any, ::Int64, ::Tx, ::Tx, ::T) where {Tx, Tdx, Tdg, T, G}
   @ Optim ~/.julia/packages/Optim/7krni/src/multivariate/solvers/first_order/l_bfgs.jl:139

where the penultimate argument is of type MVector instead of Point. I guess this is caused by the default method of similar for FieldVector. With

Base.similar(::Type{<:Point}, ::Type{T}) where T = Point(zero(T), zero(T))

it works:

julia> sol = solve(prob, LBFGS())
retcode: Success
u: 2-element Point{Float64} with indices SOneTo(2):
 0.999999999999928
 0.9999999999998559

One could also add a constructor for Point that doesn’t initialize the fields.

1 Like

You are absolutely right, this works! It is suggested in the FieldVector documentation to write a Base.similar method. I missed it.