Using MathOptInterface with custom NL objective

I am having trouble passing a custom objective function to MOI. Here’s an example:

struct dumbEval <: MOI.AbstractNLPEvaluator end

e = dumbEval();

nlpBlockData = MOI.NLPBlockData([], e, true);

o = Ipopt.Optimizer();

x = MOI.add_variable(o);

MOI.set(o, MOI.NLPBlock(), nlpBlockData)

function MOI.initialize(e::dumbEval, requested_features::Vector{Symbol})
    return nothing

function MOI.features_available(e::dumbEval)
    return []

function MOI.eval_objective(e::dumbEval, x)
    return 1.0


Ipopt evaluates the objective at 0.0, instead of 1.0. What am I doing wrong?

I strongly encourage you to use JuMP instead of MOI directly (or other Julia packages like Optim, GalacticOptim, etc.).

The MathOptInterface API is complex to implement, and even more so for nonlinear problems.
You had a few things missing:

  • You did not check requested_features to see what Ipopt asked for (it asked for :grad)
  • Thus features_available should return [:grad], and you needed to implement eval_objective_gradient
  • You need to set an ObjectiveSense

Here’s is the example working, but again, I would encourage you to use JuMP instead.

using Ipopt
const MOI = Ipopt.MOI
struct dumbEval <: MOI.AbstractNLPEvaluator end
MOI.initialize(::dumbEval, ::Vector{Symbol}) = nothing
MOI.features_available(::dumbEval) = [:Grad]
MOI.eval_objective(::dumbEval, ::Vector{Float64}) = 1.0
function MOI.eval_objective_gradient(
    g .= 0.0
o = Ipopt.Optimizer()
x = MOI.add_variable(o)
MOI.set(o, MOI.NLPBlock(), MOI.NLPBlockData([], dumbEval(), true))
MOI.set(o, MOI.ObjectiveSense(), MOI.MIN_SENSE)

Thank you for the clarification. I was missing

MOI.set(optimizer, MOI.ObjectiveSense(), MOI.MIN_SENSE)

which I was assuming would just default to MIN…
I appreciate how much easier JuMP is to use, but I want (custom) Hessians of nonlinear, multivariate functions, which only seem to be possible through MOI directly.