|
1 |
| -function simplify_constants(O::Operation, shorten_tree) |
2 |
| - while true |
3 |
| - O′ = _simplify_constants(O, shorten_tree) |
4 |
| - if is_operation(O′) |
5 |
| - O′ = Operation(O′.op, simplify_constants.(O′.args, shorten_tree)) |
6 |
| - end |
7 |
| - isequal(O, O′) && return O |
8 |
| - O = O′ |
| 1 | +import SymbolicUtils |
| 2 | +import SymbolicUtils: FnType |
| 3 | + |
| 4 | +# ModelingToolkit -> SymbolicUtils |
| 5 | +SymbolicUtils.istree(x::Operation) = true |
| 6 | +function SymbolicUtils.operation(x::Operation) |
| 7 | + if x.op isa Variable |
| 8 | + T = FnType{NTuple{length(x.args), Any}, vartype(x.op)} |
| 9 | + SymbolicUtils.Variable{T}(x.op.name) |
| 10 | + else |
| 11 | + x.op |
9 | 12 | end
|
10 | 13 | end
|
11 |
| -simplify_constants(x, shorten_tree) = x |
12 | 14 |
|
13 |
| -""" |
14 |
| -simplify_constants(x::Operation) |
| 15 | +# This is required to infer the right type for |
| 16 | +# Operation(Variable{Parameter{Number}}(:foo), []) |
| 17 | +# While keeping the metadata that the variable is a parameter. |
| 18 | +SymbolicUtils.promote_symtype(f::SymbolicUtils.Sym{FnType{X,Parameter{Y}}}, |
| 19 | + xs...) where {X, Y} = Y |
15 | 20 |
|
16 |
| -Simplifies the constants within an expression, for example removing equations |
17 |
| -multiplied by a zero and summing constant values. |
18 |
| -""" |
19 |
| -simplify_constants(x) = simplify_constants(x, true) |
| 21 | +SymbolicUtils.arguments(x::Operation) = x.args |
20 | 22 |
|
21 |
| -Base.isone(x::Operation) = x.op == one || x.op == Constant && isone(x.args) |
22 |
| -const AC_OPERATORS = (*, +) |
| 23 | +# SymbolicUtils wants raw numbers |
| 24 | +SymbolicUtils.to_symbolic(x::Constant) = x.value |
| 25 | +SymbolicUtils.to_symbolic(x::Variable{T}) where {T} = SymbolicUtils.Sym{T}(x.name) |
23 | 26 |
|
24 |
| -function _simplify_constants(O::Operation, shorten_tree) |
25 |
| - # Tree shrinking |
26 |
| - if shorten_tree && O.op ∈ AC_OPERATORS |
27 |
| - # Flatten tree |
28 |
| - idxs = findall(x -> is_operation(x) && x.op === O.op, O.args) |
29 |
| - if !isempty(idxs) |
30 |
| - keep_idxs = eachindex(O.args) .∉ (idxs,) |
31 |
| - args = Vector{Expression}[O.args[i].args for i in idxs] |
32 |
| - push!(args, O.args[keep_idxs]) |
33 |
| - return Operation(O.op, vcat(args...)) |
34 |
| - end |
| 27 | +# Optional types of vars |
| 28 | +# Once converted to SymbolicUtils Variable, a Parameter needs to hide its metadata |
| 29 | +_vartype(x::Variable{<:Parameter{T}}) where {T} = T |
| 30 | +_vartype(x::Variable{T}) where {T} = T |
| 31 | +SymbolicUtils.symtype(x::Variable) = _vartype(x) # needed for a() |
| 32 | +SymbolicUtils.symtype(x::SymbolicUtils.Sym{<:Parameter{T}}) where {T} = T |
35 | 33 |
|
36 |
| - # Collapse constants |
37 |
| - idxs = findall(is_constant, O.args) |
38 |
| - if length(idxs) > 1 |
39 |
| - other_idxs = eachindex(O.args) .∉ (idxs,) |
40 |
| - new_const = Constant(mapreduce(get, O.op, O.args[idxs])) |
41 |
| - args = push!(O.args[other_idxs], new_const) |
| 34 | +# returning Any causes SymbolicUtils to infer the type using `promote_symtype` |
| 35 | +# But we are OK with Number here for now I guess |
| 36 | +SymbolicUtils.symtype(x::Expression) = Number |
42 | 37 |
|
43 |
| - length(args) == 1 && return first(args) |
44 |
| - return Operation(O.op, args) |
45 |
| - end |
46 |
| - end |
47 |
| - |
48 |
| - if O.op === (*) |
49 |
| - # If any variable is `Constant(0)`, zero the whole thing |
50 |
| - any(iszero, O.args) && return Constant(0) |
51 |
| - |
52 |
| - # If any variable is `Constant(1)`, remove that `Constant(1)` unless |
53 |
| - # they are all `Constant(1)`, in which case simplify to a single variable |
54 |
| - if any(isone, O.args) |
55 |
| - args = filter(!isone, O.args) |
56 |
| - |
57 |
| - isempty(args) && return Constant(1) |
58 |
| - length(args) == 1 && return first(args) |
59 |
| - return Operation(O.op, args) |
60 |
| - end |
61 |
| - |
62 |
| - return O |
63 |
| - end |
64 |
| - |
65 |
| - if O.op === (^) && length(O.args) == 2 && iszero(O.args[2]) |
66 |
| - return Constant(1) |
67 |
| - end |
68 | 38 |
|
69 |
| - if O.op === (^) && length(O.args) == 2 && isone(O.args[2]) |
70 |
| - return O.args[1] |
71 |
| - end |
72 |
| - |
73 |
| - if O.op === (+) && any(iszero, O.args) |
74 |
| - # If there are Constant(0)s in a big `+` expression, get rid of them |
75 |
| - args = filter(!iszero, O.args) |
76 |
| - |
77 |
| - isempty(args) && return Constant(0) |
78 |
| - length(args) == 1 && return first(args) |
79 |
| - return Operation(O.op, args) |
80 |
| - end |
| 39 | +# SymbolicUtils -> ModelingToolkit |
81 | 40 |
|
82 |
| - if (O.op === (-) || O.op === (+) || O.op === (*)) && all(is_constant, O.args) && !isempty(O.args) |
83 |
| - v = O.args[1].value |
84 |
| - for i in 2:length(O.args) |
85 |
| - v = O.op(v, O.args[i].value) |
86 |
| - end |
87 |
| - return Constant(v) |
88 |
| - end |
89 |
| - |
90 |
| - (O.op, length(O.args)) === (identity, 1) && return O.args[1] |
91 |
| - |
92 |
| - (O.op, length(O.args)) === (-, 1) && return Operation(*, Expression[-1, O.args[1]]) |
| 41 | +function simplify_constants(expr) |
| 42 | + SymbolicUtils.simplify(expr) |> to_mtk |
| 43 | +end |
93 | 44 |
|
94 |
| - return O |
| 45 | +to_mtk(x) = x |
| 46 | +to_mtk(x::Number) = Constant(x) |
| 47 | +to_mtk(v::SymbolicUtils.Sym{T}) where {T} = Variable{T}(nameof(v)) |
| 48 | +to_mtk(v::SymbolicUtils.Sym{FnType{X,Y}}) where {X,Y} = Variable{Y}(nameof(v)) |
| 49 | +function to_mtk(expr::SymbolicUtils.Term) |
| 50 | + Operation(to_mtk(SymbolicUtils.operation(expr)), |
| 51 | + map(to_mtk, SymbolicUtils.arguments(expr))) |
95 | 52 | end
|
96 |
| -_simplify_constants(x, shorten_tree) = x |
97 |
| -_simplify_constants(x) = _simplify_constants(x, true) |
0 commit comments