Skip to content

Commit cfed322

Browse files
authored
[Nonlinear] improve parsing performance of Scalar(Affine,Quadratic)Function (#2487)
1 parent 68e0c7c commit cfed322

File tree

2 files changed

+118
-17
lines changed

2 files changed

+118
-17
lines changed

src/Nonlinear/parse.jl

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -265,22 +265,98 @@ end
265265
function parse_expression(
266266
data::Model,
267267
expr::Expression,
268-
x::MOI.ScalarAffineFunction,
268+
f::MOI.ScalarAffineFunction,
269269
parent_index::Int,
270270
)
271-
f = convert(MOI.ScalarNonlinearFunction, x)
272-
parse_expression(data, expr, f, parent_index)
271+
if isempty(f.terms)
272+
parse_expression(data, expr, f.constant, parent_index)
273+
return
274+
elseif iszero(f.constant) && length(f.terms) == 1
275+
# Expression of for `a * x`
276+
parse_expression(data, expr, only(f.terms), parent_index)
277+
return
278+
end
279+
id_plus = data.operators.multivariate_operator_to_id[:+]
280+
push!(expr.nodes, Node(NODE_CALL_MULTIVARIATE, id_plus, parent_index))
281+
new_parent = length(expr.nodes)
282+
for term in f.terms
283+
parse_expression(data, expr, term, new_parent)
284+
end
285+
if !iszero(f.constant)
286+
parse_expression(data, expr, f.constant, new_parent)
287+
end
273288
return
274289
end
275290

276291
function parse_expression(
277292
data::Model,
278293
expr::Expression,
279-
x::MOI.ScalarQuadraticFunction,
294+
f::MOI.ScalarQuadraticFunction,
280295
parent_index::Int,
281296
)
282-
f = convert(MOI.ScalarNonlinearFunction, x)
283-
parse_expression(data, expr, f, parent_index)
297+
if isempty(f.quadratic_terms) && isempty(f.affine_terms)
298+
parse_expression(data, expr, f.constant, parent_index)
299+
return
300+
elseif iszero(f.constant)
301+
if length(f.quadratic_terms) == 1 && isempty(f.affine_terms)
302+
parse_expression(data, expr, only(f.quadratic_terms), parent_index)
303+
return
304+
elseif isempty(f.quadratic_terms) && length(f.affine_terms) == 1
305+
parse_expression(data, expr, only(f.affine_terms), parent_index)
306+
return
307+
end
308+
end
309+
id_plus = data.operators.multivariate_operator_to_id[:+]
310+
push!(expr.nodes, Node(NODE_CALL_MULTIVARIATE, id_plus, parent_index))
311+
new_parent = length(expr.nodes)
312+
for term in f.quadratic_terms
313+
parse_expression(data, expr, term, new_parent)
314+
end
315+
for term in f.affine_terms
316+
parse_expression(data, expr, term, new_parent)
317+
end
318+
if !iszero(f.constant)
319+
parse_expression(data, expr, f.constant, new_parent)
320+
end
321+
return
322+
end
323+
324+
function parse_expression(
325+
data::Model,
326+
expr::Expression,
327+
x::MOI.ScalarAffineTerm,
328+
parent_index::Int,
329+
)
330+
if isone(x.coefficient)
331+
parse_expression(data, expr, x.variable, parent_index)
332+
else
333+
id_mul = data.operators.multivariate_operator_to_id[:*]
334+
push!(expr.nodes, Node(NODE_CALL_MULTIVARIATE, id_mul, parent_index))
335+
mul_parent = length(expr.nodes)
336+
parse_expression(data, expr, x.coefficient, mul_parent)
337+
parse_expression(data, expr, x.variable, mul_parent)
338+
end
339+
return
340+
end
341+
342+
function parse_expression(
343+
data::Model,
344+
expr::Expression,
345+
x::MOI.ScalarQuadraticTerm,
346+
parent_index::Int,
347+
)
348+
id_mul = data.operators.multivariate_operator_to_id[:*]
349+
push!(expr.nodes, Node(NODE_CALL_MULTIVARIATE, id_mul, parent_index))
350+
mul_parent = length(expr.nodes)
351+
coef = x.coefficient
352+
if x.variable_1 == x.variable_2
353+
coef /= 2
354+
end
355+
if !isone(coef)
356+
parse_expression(data, expr, coef, mul_parent)
357+
end
358+
parse_expression(data, expr, x.variable_1, mul_parent)
359+
parse_expression(data, expr, x.variable_2, mul_parent)
284360
return
285361
end
286362

test/Nonlinear/Nonlinear.jl

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,24 +1006,49 @@ end
10061006
function test_scalar_nonlinear_function_parse_scalaraffinefunction()
10071007
model = MOI.Utilities.Model{Float64}()
10081008
x = MOI.add_variable(model)
1009-
f = 1.0 * x + 2.0
1010-
nlp_model = MOI.Nonlinear.Model()
1011-
e1 = MOI.Nonlinear.add_expression(nlp_model, f)
1012-
e2 = MOI.Nonlinear.add_expression(nlp_model, :(1.0 * $x + 2.0))
1013-
@test nlp_model[e1] == nlp_model[e2]
1009+
y = MOI.add_variable(model)
1010+
terms = MOI.ScalarAffineTerm{Float64}[]
1011+
for (f, expr) in (
1012+
MOI.ScalarAffineFunction(terms, 0.0) => :(0.0),
1013+
MOI.ScalarAffineFunction(terms, 1.0) => :(1.0),
1014+
(1.0 * x + 2.0) => :($x + 2.0),
1015+
(2.0 * x + 2.0) => :(2.0 * $x + 2.0),
1016+
(2.0 * x + -3.0 * y) => :(2.0 * $x + -3.0 * $y),
1017+
(2.0 * x) => :(2.0 * $x),
1018+
(1.0 * x) => :($x),
1019+
)
1020+
nlp_model = MOI.Nonlinear.Model()
1021+
f1 = MOI.Nonlinear.add_expression(nlp_model, f)
1022+
f2 = MOI.Nonlinear.add_expression(nlp_model, expr)
1023+
@test nlp_model[f1] == nlp_model[f2]
1024+
end
10141025
return
10151026
end
10161027

10171028
function test_scalar_nonlinear_function_parse_scalarquadraticfunction()
10181029
model = MOI.Utilities.Model{Float64}()
10191030
x = MOI.add_variable(model)
10201031
y = MOI.add_variable(model)
1021-
f = 1.5 * x * x + 2.5 * x * y + 3.5 * x + 2.0
1022-
nlp_model = MOI.Nonlinear.Model()
1023-
e1 = MOI.Nonlinear.add_expression(nlp_model, f)
1024-
f_expr = :(1.5 * $x * $x + 2.5 * $x * $y + 3.5 * $x + 2.0)
1025-
e2 = MOI.Nonlinear.add_expression(nlp_model, f_expr)
1026-
@test nlp_model[e1] == nlp_model[e2]
1032+
terms = MOI.ScalarAffineTerm{Float64}[]
1033+
qterms = MOI.ScalarQuadraticTerm{Float64}[]
1034+
aterm = MOI.ScalarAffineTerm(2.0, x)
1035+
for (f, expr) in (
1036+
MOI.ScalarQuadraticFunction(qterms, terms, 0.0) => :(0.0),
1037+
MOI.ScalarQuadraticFunction(qterms, terms, 1.0) => :(1.0),
1038+
MOI.ScalarQuadraticFunction(qterms, [aterm], 0.0) => :(2.0 * $x),
1039+
(1.0 * x * x + 1.0 * x + 1.0) => :($x * $x + $x + 1),
1040+
(1.0 * x * x + 1.0 * x) => :($x * $x + $x),
1041+
(1.0 * x * x + 2.0 * x) => :($x * $x + 2.0 * $x),
1042+
(2.0 * x * x + 2.0 * x) => :(2.0 * $x * $x + 2.0 * $x),
1043+
(1.0 * x * x) => :($x * $x),
1044+
(1.5 * x * x + 2.5 * x * y + 3.5 * x + 2.0) =>
1045+
:(1.5 * $x * $x + 2.5 * $x * $y + 3.5 * $x + 2.0),
1046+
)
1047+
nlp_model = MOI.Nonlinear.Model()
1048+
f1 = MOI.Nonlinear.add_expression(nlp_model, f)
1049+
f2 = MOI.Nonlinear.add_expression(nlp_model, expr)
1050+
@test nlp_model[f1] == nlp_model[f2]
1051+
end
10271052
return
10281053
end
10291054

0 commit comments

Comments
 (0)