Skip to content

Commit

Permalink
Fix empty sums in Aff/QuadExpr parse_expression (#3091)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Sep 28, 2022
1 parent 4e276d9 commit 2a587d0
Show file tree
Hide file tree
Showing 2 changed files with 98 additions and 36 deletions.
70 changes: 35 additions & 35 deletions src/nlp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,28 @@ function MOI.Nonlinear.parse_expression(
x::GenericAffExpr,
parent::Int,
)
push!(
expr.nodes,
MOI.Nonlinear.Node(
MOI.Nonlinear.NODE_CALL_MULTIVARIATE,
model.operators.multivariate_operator_to_id[:+],
parent,
),
)
sum_parent = length(expr.nodes)
sum_id = model.operators.multivariate_operator_to_id[:+]
prod_id = model.operators.multivariate_operator_to_id[:*]
call_multi = MOI.Nonlinear.NODE_CALL_MULTIVARIATE
n_terms = length(x.terms) + !iszero(x.constant)
if n_terms == 0
# If `x` is empty, then substitute `0.0` as the expression.
MOI.Nonlinear.parse_expression(model, expr, 0.0, parent)
return
elseif n_terms == 1
# If `x` contains 1 term, then we don't need the leading `+` operator.
else
push!(expr.nodes, MOI.Nonlinear.Node(call_multi, sum_id, parent))
parent = length(expr.nodes)
end
if !iszero(x.constant)
MOI.Nonlinear.parse_expression(model, expr, x.constant, sum_parent)
MOI.Nonlinear.parse_expression(model, expr, x.constant, parent)
end
for (v, c) in x.terms
if isone(c)
MOI.Nonlinear.parse_expression(model, expr, v, sum_parent)
MOI.Nonlinear.parse_expression(model, expr, v, parent)
else
push!(
expr.nodes,
MOI.Nonlinear.Node(
MOI.Nonlinear.NODE_CALL_MULTIVARIATE,
model.operators.multivariate_operator_to_id[:*],
sum_parent,
),
)
push!(expr.nodes, MOI.Nonlinear.Node(call_multi, prod_id, parent))
mult_parent = length(expr.nodes)
MOI.Nonlinear.parse_expression(model, expr, c, mult_parent)
MOI.Nonlinear.parse_expression(model, expr, v, mult_parent)
Expand All @@ -102,23 +100,25 @@ function MOI.Nonlinear.parse_expression(
x::QuadExpr,
parent::Int,
)
sum = model.operators.multivariate_operator_to_id[:+]
prod = model.operators.multivariate_operator_to_id[:*]
push!(
expr.nodes,
MOI.Nonlinear.Node(MOI.Nonlinear.NODE_CALL_MULTIVARIATE, sum, parent),
)
sum_parent = length(expr.nodes)
MOI.Nonlinear.parse_expression(model, expr, x.aff, sum_parent)
sum_id = model.operators.multivariate_operator_to_id[:+]
prod_id = model.operators.multivariate_operator_to_id[:*]
call_multi = MOI.Nonlinear.NODE_CALL_MULTIVARIATE
n_terms = length(x.terms) + !iszero(x.aff)
if n_terms == 0
# If `x` is empty, then substitute `0.0` as the expression.
MOI.Nonlinear.parse_expression(model, expr, 0.0, parent)
return
elseif n_terms == 1
# If `x` contains 1 term, then we don't need the leading `+` operator.
else
push!(expr.nodes, MOI.Nonlinear.Node(call_multi, sum_id, parent))
parent = length(expr.nodes)
end
if !iszero(x.aff)
MOI.Nonlinear.parse_expression(model, expr, x.aff, parent)
end
for (xy, c) in x.terms
push!(
expr.nodes,
MOI.Nonlinear.Node(
MOI.Nonlinear.NODE_CALL_MULTIVARIATE,
prod,
sum_parent,
),
)
push!(expr.nodes, MOI.Nonlinear.Node(call_multi, prod_id, parent))
mult_parent = length(expr.nodes)
MOI.Nonlinear.parse_expression(model, expr, xy.a, mult_parent)
MOI.Nonlinear.parse_expression(model, expr, xy.b, mult_parent)
Expand Down
64 changes: 63 additions & 1 deletion test/nlp.jl
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ function test_non_macro_nonlinear_functions()
@test MOI.constraint_expr(d, 5) ==
:((-3.0 + x[$xidx] + 2.0 * x[$yidx]) - 1.0 == 0.0)
@test MOI.constraint_expr(d, 6) == :(
(+(-1.0 * x[$xidx]) + x[$xidx] * x[$xidx] + x[$yidx] * x[$yidx] * 2.0) -
(-1.0 * x[$xidx] + x[$xidx] * x[$xidx] + x[$yidx] * x[$yidx] * 2.0) -
1.0 == 0.0
)
return
Expand Down Expand Up @@ -1512,6 +1512,68 @@ function test_nonlinear_model()
return
end

function test_parse_expression_affexpr_empty()
model = Model()
@variable(model, x)
expr = AffExpr()
@NLexpression(model, ref, 0)
nlp = nonlinear_model(model)
@test MOI.Nonlinear.parse_expression(nlp, expr) == nlp[index(ref)]
return
end

function test_parse_expression_affexpr_univariate_sum()
model = Model()
@variable(model, x)
@expression(model, expr, 1 * x)
@NLexpression(model, ref, x)
nlp = nonlinear_model(model)
@test MOI.Nonlinear.parse_expression(nlp, expr) == nlp[index(ref)]
return
end

function test_parse_expression_affexpr_multivariate_sum()
model = Model()
@variable(model, x)
@variable(model, y)
@expression(model, expr, x + y)
@NLexpression(model, ref, +(x, y))
nlp = nonlinear_model(model)
@test MOI.Nonlinear.parse_expression(nlp, expr) == nlp[index(ref)]
return
end

function test_parse_expression_quadexpr_empty()
model = Model()
@variable(model, x)
expr = QuadExpr()
@NLexpression(model, ref, 0)
nlp = nonlinear_model(model)
@test MOI.Nonlinear.parse_expression(nlp, expr) == nlp[index(ref)]
return
end

function test_parse_expression_quadexpr_univariate_sum()
model = Model()
@variable(model, x)
@expression(model, expr, x^2)
@NLexpression(model, ref, x * x)
nlp = nonlinear_model(model)
@test MOI.Nonlinear.parse_expression(nlp, expr) == nlp[index(ref)]
return
end

function test_parse_expression_quadexpr_multivariate_sum()
model = Model()
@variable(model, x)
@variable(model, y)
@expression(model, expr, x^2 + y^2)
@NLexpression(model, ref, +(x * x, y * y))
nlp = nonlinear_model(model)
@test MOI.Nonlinear.parse_expression(nlp, expr) == nlp[index(ref)]
return
end

end

TestNLP.runtests()

0 comments on commit 2a587d0

Please sign in to comment.