@@ -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