Skip to content

Commit

Permalink
Fix bug in handling of off-diagonal QP terms in ScalarNonlinearFuncti…
Browse files Browse the repository at this point in the history
…on (#594)
  • Loading branch information
odow authored Nov 28, 2024
1 parent 1c31d31 commit deab7ff
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 18 deletions.
35 changes: 19 additions & 16 deletions src/MOI_wrapper/MOI_nonlinear.jl
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,24 @@ function _add_expression_tree_node(
append!(data, -1.0)
append!(parent, parent_index)
multiply_parent_index = Cint(length(opcode) - 1)
_add_expression_tree_node(
model,
stack,
opcode,
data,
parent,
term.coefficient,
multiply_parent_index,
)
# https://jump.dev/MathOptInterface.jl/stable/reference/standard_form
# ScalarQuadraticFunction stores diagonal ScalarQuadraticTerms multiplied by
# 2
coeff = term.coefficient
if term.variable_1 == term.variable_2
coeff /= 2
end
if !isone(coeff)
_add_expression_tree_node(
model,
stack,
opcode,
data,
parent,
coeff,
multiply_parent_index,
)
end
_add_expression_tree_node(
model,
stack,
Expand Down Expand Up @@ -274,19 +283,13 @@ function _add_expression_tree_node(
)
end
for term in s.quadratic_terms
# https://jump.dev/MathOptInterface.jl/stable/reference/standard_form
# ScalarQuadraticFunction stores ScalarQuadraticTerms multiplied by 2
_add_expression_tree_node(
model,
stack,
opcode,
data,
parent,
MOI.ScalarQuadraticTerm{Float64}(
0.5 * term.coefficient,
term.variable_1,
term.variable_2,
),
term,
plus_parent_index,
)
end
Expand Down
26 changes: 24 additions & 2 deletions test/MOI/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1352,8 +1352,8 @@ function test_nonlinear_quadratic_3()
MOI.set(model, MOI.ObjectiveSense(), MOI.MAX_SENSE)
f = 1.0 * x + 1.0 * y
MOI.set(model, MOI.ObjectiveFunction{typeof(f)}(), f)
f1 = MOI.ScalarQuadraticTerm{Float64}(1.0, x, x)
f2 = MOI.ScalarQuadraticTerm{Float64}(1.0, y, y)
f1 = MOI.ScalarQuadraticTerm{Float64}(2.0, x, x)
f2 = MOI.ScalarQuadraticTerm{Float64}(2.0, y, y)
f3 = MOI.ScalarNonlinearFunction(:+, Any[f1, f2])
g = MOI.ScalarNonlinearFunction(:sqrt, Any[f3])
c = MOI.add_constraint(model, g, MOI.LessThan(1.0))
Expand Down Expand Up @@ -1456,6 +1456,28 @@ function test_delete_nonlinear_index()
return
end

function test_scalar_quadratic_function_with_off_diag_in_scalar_nonlinear()
if !Gurobi._supports_nonlinear()
return
end
for (a, b, status) in [
(1.0, 2.0, MOI.OPTIMAL),
(1.0, 3.0, MOI.INFEASIBLE),
(2.0, 3.0, MOI.OPTIMAL),
(2.0, 4.0, MOI.INFEASIBLE),
]
model = Gurobi.Optimizer(GRB_ENV)
MOI.set(model, MOI.Silent(), true)
x, _ = MOI.add_constrained_variable(model, MOI.EqualTo(2.0))
y, _ = MOI.add_constrained_variable(model, MOI.EqualTo(3.0))
f = MOI.ScalarNonlinearFunction(:sqrt, Any[a*x*y])
MOI.add_constraint(model, f, MOI.GreaterThan(b))
MOI.optimize!(model)
@test MOI.get(model, MOI.TerminationStatus()) == status
end
return
end

end # TestMOIWrapper

TestMOIWrapper.runtests()

0 comments on commit deab7ff

Please sign in to comment.