Skip to content

Commit b22a6d5

Browse files
committed
integrate SymbolicUtils
1 parent f0b733f commit b22a6d5

File tree

2 files changed

+40
-85
lines changed

2 files changed

+40
-85
lines changed

src/ModelingToolkit.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ include("variables.jl")
5858
include("context_dsl.jl")
5959
include("operations.jl")
6060
include("differentials.jl")
61+
include("symutils.jl")
6162

6263
function Base.convert(::Type{Variable},x::Operation)
6364
if x.op isa Variable

src/simplify.jl

Lines changed: 39 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,97 +1,51 @@
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
912
end
1013
end
11-
simplify_constants(x, shorten_tree) = x
1214

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.Variable{FnType{X,Parameter{Y}}},
19+
xs...) where {X, Y} = Y
1520

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
2022

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.Variable{T}(x.name)
2326

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.Variable{<:Parameter{T}}) where {T} = T
3533

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
4237

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
6838

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
8140

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
9344

94-
return O
45+
to_mtk(x) = x
46+
to_mtk(v::SymbolicUtils.Variable{T}) where {T} = Variable{T}(nameof(v))
47+
to_mtk(v::SymbolicUtils.Variable{FnType{X,Y}}) where {X,Y} = Variable{Y}(nameof(v))
48+
function to_mtk(expr::SymbolicUtils.Term)
49+
Operation(to_mtk(SymbolicUtils.operation(expr)),
50+
map(to_mtk, SymbolicUtils.arguments(expr)))
9551
end
96-
_simplify_constants(x, shorten_tree) = x
97-
_simplify_constants(x) = _simplify_constants(x, true)

0 commit comments

Comments
 (0)