@objective and @constraint seem to be reformulating x^2 to x*x in nonlinear expressions, which leads to poorer McCormick relaxations. This isn't an issue when using the legacy macros of @NLobjective and @NLconstraint. I suspect this happens somewhere in
|
function MOI.add_constraint(m::Optimizer, f::F, s::S) where {F<:Union{SAF,SQF,MOI.ScalarNonlinearFunction},S<:INEQ_SETS} |
|
check_inbounds!(m, f) |
|
ci = CI{F, S}(m._input_problem._constraint_count += 1) |
|
if f isa MOI.ScalarNonlinearFunction |
|
if isnothing(m._input_problem._nlp_data) |
|
model = MOI.Nonlinear.Model() |
|
backend = MOI.Nonlinear.SparseReverseMode() |
|
vars = MOI.get(m, MOI.ListOfVariableIndices()) |
|
evaluator = MOI.Nonlinear.Evaluator(model, backend, vars) |
|
m._input_problem._nlp_data = MOI.NLPBlockData(evaluator) |
|
end |
|
MOI.Nonlinear.add_constraint(m._input_problem._nlp_data.evaluator.model, f, s) |
|
constraint_bounds = m._input_problem._nlp_data.constraint_bounds |
|
has_objective = m._input_problem._nlp_data.has_objective |
|
if s isa MOI.LessThan |
|
push!(constraint_bounds, MOI.NLPBoundsPair(-Inf, s.upper)) |
|
elseif s isa MOI.GreaterThan |
|
push!(constraint_bounds, MOI.NLPBoundsPair(s.lower, Inf)) |
|
else |
|
push!(constraint_bounds, MOI.NLPBoundsPair(s.value, s.value)) |
|
end |
|
m._input_problem._nlp_data = MOI.NLPBlockData(constraint_bounds, m._input_problem._nlp_data.evaluator, has_objective) |
|
else |
|
_constraints(m, F, S)[ci] = (f, s) |
|
end |
|
return ci |
|
end |
and
|
function MOI.set(m::Optimizer, ::MOI.ObjectiveFunction{T}, f::T) where T <: Union{VI,SAF,SQF,MOI.ScalarNonlinearFunction} |
|
check_inbounds!(m, f) |
|
if f isa MOI.ScalarNonlinearFunction |
|
if isnothing(m._input_problem._nlp_data) |
|
model = MOI.Nonlinear.Model() |
|
MOI.Nonlinear.set_objective(model, f) |
|
backend = MOI.Nonlinear.SparseReverseMode() |
|
vars = MOI.get(m, MOI.ListOfVariableIndices()) |
|
evaluator = MOI.Nonlinear.Evaluator(model, backend, vars) |
|
m._input_problem._nlp_data = MOI.NLPBlockData(evaluator) |
|
else |
|
MOI.Nonlinear.set_objective(m._input_problem._nlp_data.evaluator.model, f) |
|
m._input_problem._nlp_data = MOI.NLPBlockData(m._input_problem._nlp_data.evaluator) |
|
end |
|
else |
|
m._input_problem._objective = f |
|
end |
|
end |
Example:
model = JuMP.Model(EAGO.Optimizer)
@variable(model, x[1:2])
@objective(model, Min, x[1]^2 + x[2]^3)
JuMP.optimize!(model)
returns
MathOptInterface.Nonlinear.Expression(
MathOptInterface.Nonlinear.Node[
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 2, -1),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 1, 1),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 3, 2),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 1, 3),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 1, 3),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 4, 2),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 2, 6),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_VALUE, 1, 6),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 3, 1)
],
[3.0]
)
model = JuMP.Model(EAGO.Optimizer)
@variable(model, x[1:2])
@NLobjective(model, Min, x[1]^2 + x[2]^3)
JuMP.optimize!(model)
returns
MathOptInterface.Nonlinear.Expression(
MathOptInterface.Nonlinear.Node[
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 2, -1),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 1, 1),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 4, 2),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 1, 3),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_VALUE, 1, 3),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_CALL_MULTIVARIATE, 4, 2),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 2, 6),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_VALUE, 2, 6),
MathOptInterface.Nonlinear.Node(MathOptInterface.Nonlinear.NODE_MOI_VARIABLE, 3, 1)
],
[2.0, 3.0]
)
(i.e., x^2 + x^3, as expected).
@objectiveand@constraintseem to be reformulatingx^2tox*xin nonlinear expressions, which leads to poorer McCormick relaxations. This isn't an issue when using the legacy macros of@NLobjectiveand@NLconstraint. I suspect this happens somewhere inEAGO.jl/src/eago_optimizer/moi_wrapper.jl
Lines 74 to 100 in 15bb6cb
EAGO.jl/src/eago_optimizer/moi_wrapper.jl
Lines 277 to 294 in 15bb6cb
Example:
returns
returns
(i.e.,
x^2 + x^3, as expected).