@@ -1446,6 +1446,94 @@ function test_intercept_ForwardDiff_MethodError()
14461446 return
14471447end
14481448
1449+ function test_extract_subexpression()
1450+ model = Nonlinear.Model()
1451+ x = MOI.VariableIndex(1)
1452+ sub = MOI.ScalarNonlinearFunction(:^, Any[x, 3])
1453+ f = MOI.ScalarNonlinearFunction(:+, Any[sub, sub])
1454+ expr = Nonlinear.parse_expression(model, f)
1455+ display(expr.nodes)
1456+ @test expr == Nonlinear.Expression(
1457+ [
1458+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 1, -1),
1459+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 1, 1),
1460+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 1, 1),
1461+ ],
1462+ Float64[],
1463+ )
1464+ expected_sub = Nonlinear.Expression(
1465+ [
1466+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 4, -1)
1467+ Nonlinear.Node(Nonlinear.NODE_MOI_VARIABLE, 1, 1)
1468+ Nonlinear.Node(Nonlinear.NODE_VALUE, 1, 1)
1469+ ],
1470+ [3.0],
1471+ )
1472+ @test model.expressions == [expected_sub]
1473+ @test model.cache[sub] == Nonlinear.ExpressionIndex(1)
1474+
1475+ h = MOI.ScalarNonlinearFunction(:*, Any[2, sub, 1])
1476+ g = MOI.ScalarNonlinearFunction(:+, Any[sub, h])
1477+ expr = MOI.Nonlinear.parse_expression(model, g)
1478+ expected_g = Nonlinear.Expression(
1479+ [
1480+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 1, -1)
1481+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 1, 1)
1482+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 3, 1)
1483+ Nonlinear.Node(Nonlinear.NODE_VALUE, 1, 3)
1484+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 1, 3)
1485+ Nonlinear.Node(Nonlinear.NODE_VALUE, 2, 3)
1486+ ],
1487+ [2.0, 1.0],
1488+ )
1489+ @test expr == expected_g
1490+ # It should have detected the sub-expressions that was the same as `f`
1491+ @test model.expressions == [expected_sub]
1492+ # This means that it didn't get to extract from `g`, let's also test
1493+ # with extraction by starting with an empty model
1494+
1495+ model = Nonlinear.Model()
1496+ MOI.Nonlinear.set_objective(model, g)
1497+ @test model.objective == expected_g
1498+ @test model.expressions == [expected_sub]
1499+ # Test that the objective function gets rewritten as we reuse `h`
1500+ # Also test that we don't change the parents in the stack of `h`
1501+ # by creating a long stack
1502+ prod = MOI.ScalarNonlinearFunction(:*, [h, x])
1503+ sum = MOI.ScalarNonlinearFunction(:*, [x, x, x, x, prod])
1504+ expr = Nonlinear.parse_expression(model, sum)
1505+ @test isempty(model.objective.values)
1506+ @test model.objective.nodes == [
1507+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 1, -1),
1508+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 1, 1),
1509+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 2, 1),
1510+ ]
1511+ @test model.expressions == [
1512+ expected_sub,
1513+ Nonlinear.Expression(
1514+ [
1515+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 3, -1),
1516+ Nonlinear.Node(Nonlinear.NODE_VALUE, 1, 1),
1517+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 1, 1),
1518+ Nonlinear.Node(Nonlinear.NODE_VALUE, 2, 1),
1519+ ],
1520+ [2.0, 1.0],
1521+ ),
1522+ ]
1523+ @test isempty(expr.values)
1524+ @test expr.nodes == [
1525+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 3, -1),
1526+ Nonlinear.Node(Nonlinear.NODE_MOI_VARIABLE, 1, 1),
1527+ Nonlinear.Node(Nonlinear.NODE_MOI_VARIABLE, 1, 1),
1528+ Nonlinear.Node(Nonlinear.NODE_MOI_VARIABLE, 1, 1),
1529+ Nonlinear.Node(Nonlinear.NODE_MOI_VARIABLE, 1, 1),
1530+ Nonlinear.Node(Nonlinear.NODE_CALL_MULTIVARIATE, 3, 1),
1531+ Nonlinear.Node(Nonlinear.NODE_SUBEXPRESSION, 2, 6),
1532+ Nonlinear.Node(Nonlinear.NODE_MOI_VARIABLE, 1, 6),
1533+ ]
1534+ return
1535+ end
1536+
14491537end # TestNonlinear
14501538
14511539TestNonlinear.runtests()
0 commit comments