Skip to content

Commit 4d85a2a

Browse files
authored
[Utilities] fix use of recursion in canonicalize!(::ScalarNonlinearFunction) (#3009)
1 parent 03d9ada commit 4d85a2a

2 files changed

Lines changed: 24 additions & 3 deletions

File tree

src/Utilities/functions.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,9 +1072,17 @@ function canonicalize!(
10721072
end
10731073

10741074
function canonicalize!(f::MOI.ScalarNonlinearFunction)
1075-
for (i, arg) in enumerate(f.args)
1076-
if !is_canonical(arg)
1077-
f.args[i] = canonicalize!(arg)
1075+
# Don't use recursion here. This gets called for all scalar nonlinear
1076+
# constraints.
1077+
stack = MOI.ScalarNonlinearFunction[f]
1078+
while !isempty(stack)
1079+
g = pop!(stack)
1080+
for (i, arg) in enumerate(g.args)
1081+
if arg isa MOI.ScalarNonlinearFunction
1082+
push!(stack, arg)
1083+
elseif !is_canonical(arg)
1084+
g.args[i] = canonicalize!(arg)
1085+
end
10781086
end
10791087
end
10801088
return f

test/Utilities/test_functions.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2044,6 +2044,19 @@ function test_ScalarNonlinearFunction_is_canonical()
20442044
f = MOI.ScalarNonlinearFunction(:sin, Any[f])
20452045
end
20462046
@test MOI.Utilities.is_canonical(f)
2047+
@test MOI.Utilities.canonicalize!(f) isa MOI.ScalarNonlinearFunction
2048+
g = MOI.ScalarNonlinearFunction(:^, Any[1.0 * x + 1.0 * x, 2])
2049+
for _ in 1:100_000
2050+
g = MOI.ScalarNonlinearFunction(:sin, Any[g])
2051+
end
2052+
@test !MOI.Utilities.is_canonical(g)
2053+
MOI.Utilities.canonicalize!(g)
2054+
h = MOI.ScalarNonlinearFunction(:^, Any[2.0 * x, 2])
2055+
for _ in 1:100_000
2056+
h = MOI.ScalarNonlinearFunction(:sin, Any[h])
2057+
end
2058+
@test MOI.Utilities.is_canonical(h)
2059+
@test isapprox(g, h)
20472060
return
20482061
end
20492062

0 commit comments

Comments
 (0)