Skip to content

Legacy and standard constraint/objective macros represent ^2 differently in nonlinear expressions #150

@DimitriAlston

Description

@DimitriAlston

@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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions