Skip to content

Commit 5115ec1

Browse files
committed
feat: update to symbolics V7 and SymbolicUtils v4
1 parent afa08f2 commit 5115ec1

12 files changed

Lines changed: 403 additions & 55 deletions

.github/workflows/Tests.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ jobs:
4040
fail-fast: false
4141
matrix:
4242
version:
43-
- 'pre'
4443
- 'lts'
4544
- '1'
4645
os:

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Test = "1.10"
2121
OrderedCollections = "1.8"
2222
Aqua = "0.8.11"
2323
ExplicitImports = "1.11"
24-
JET = "0.9.18, 0.10.0"
24+
JET = "0.9.18, 0.10.0, 0.11"
2525
CheckConcreteStructs = "0.1.0"
2626

2727
[extras]

src/DifferentialEquation.jl

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,10 @@ corresponding to second-order differential equations.
163163
function is_rearranged_standard(eom::DifferentialEquation, degree=2)
164164
tvar = get_independent_variables(eom)[1]
165165
D = Differential(tvar)^degree
166-
return isequal(getfield.(values(eom.equations), :lhs), D.(get_variables(eom)))
166+
lhs = getfield.(values(eom.equations), :lhs)
167+
rhs = D.(get_variables(eom))
168+
diffs = Symbolics.simplify.(lhs .- rhs)
169+
return all(is_literal_zero, diffs)
167170
end
168171

169172
"""
@@ -195,7 +198,17 @@ function rearrange!(eom::DifferentialEquation, new_lhs::Vector{Num})
195198
return nothing
196199
end
197200
function get_variables_nums(vars::Vector{Num})
198-
unique(flatten([Num.(get_variables(x)) for x in vars]))
201+
# Symbolics v7: `get_variables(Differential(t, n)(x(t)))` returns the derivative term
202+
# itself, so we must explicitly strip derivatives to recover the dependent variable.
203+
out = Num[]
204+
for expr in vars
205+
sym = Symbolics.unwrap(expr)
206+
while Symbolics.is_derivative(sym)
207+
sym = first(Symbolics.arguments(sym))
208+
end
209+
push!(out, Num(sym))
210+
end
211+
return out
199212
end # TODO: remove this function or at least better names
200213

201214
"""

src/HarmonicEquation.jl

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,23 @@ end
6262

6363
"Get the parameters (not time nor variables) of a HarmonicEquation"
6464
function _parameters(eom::HarmonicEquation)
65-
all_symbols = flatten([
66-
cat(get_variables(eq.lhs), get_variables(eq.rhs); dims=1) for eq in eom.equations
67-
])
68-
# subtract the set of independent variables (i.e., time) from all free symbols
69-
return setdiff(all_symbols, get_variables(eom), get_independent_variables(eom))
65+
symbols = Num[]
66+
for eq in eom.equations
67+
vars = union(Symbolics.get_variables(eq.lhs), Symbolics.get_variables(eq.rhs))
68+
vars = sort!(collect(vars); by=string)
69+
for sym in vars
70+
if Symbolics.is_derivative(sym)
71+
sym = first(Symbolics.arguments(sym))
72+
end
73+
push!(symbols, Num(sym))
74+
end
75+
end
76+
vars = Set(get_variables(eom))
77+
indep = Set(get_independent_variables(eom))
78+
params = filter(s -> !(s in vars || s in indep), symbols)
79+
params = unique(params)
80+
sort!(params; by=string)
81+
return params
7082
end
7183

7284
"""

src/Symbolics/Symbolics_utils.jl

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
expand_all(x::Num) = Num(expand_all(x.val))
33
_apply_termwise(f, x::Num) = wrap(_apply_termwise(f, unwrap(x)))
44

5+
# Symbolics v7 note: `unwrap` now always returns `BasicSymbolic`; use `Symbolics.value`
6+
# when you want the underlying literal number (via `Const`).
7+
_literal_number(x::Num) = (v = Symbolics.value(x); v isa Number ? v : nothing)
8+
_literal_number(x::BasicSymbolic) =
9+
(v = SymbolicUtils.unwrap_const(x); v isa Number ? v : nothing)
10+
is_literal_zero(x) = (v = _literal_number(x); v !== nothing && iszero(v))
11+
512
"Expands using SymbolicUtils.expand and expand_exp_power (changes exp(x)^n to exp(x*n)"
613
function expand_all(x)
714
result = Postwalk(expand_exp_power)(SymbolicUtils.expand(x))
@@ -15,7 +22,14 @@ function expand_fraction(x::BasicSymbolic)
1522
elseif ismul(x)
1623
return _apply_termwise(expand_fraction, x)
1724
elseif isdiv(x)
18-
return sum([arg / x.den for arg in arguments(x.num)])
25+
# Only distribute division over addition in the numerator.
26+
# In SymbolicUtils v4, `arguments(x.num)` for a multiplication returns factors,
27+
# so splitting unconditionally would produce incorrect results (e.g. d*(a+b)/c).
28+
num, den = x.num, x.den
29+
if isadd(num)
30+
return sum([expand_fraction(arg) / den for arg in arguments(num)])
31+
end
32+
return expand_fraction(num) / expand_fraction(den)
1933
else
2034
return x
2135
end
@@ -131,7 +145,7 @@ function is_harmonic(x::Num, t::Num)::Bool
131145
isempty(t_terms) && return true
132146
trigs = is_trig.(t_terms)
133147

134-
if !prod(trigs)
148+
if !all(trigs)
135149
return false
136150
else
137151
powers = [max_power(first(arguments(term.val)), t) for term in t_terms[trigs]]

src/Symbolics/drop_powers.jl

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,14 @@ function drop_powers(expr::Num, vars::Vector{Num}, deg::Int)
1717
Symbolics.@variables ϵ
1818
subs_expr = deepcopy(expr)
1919
rules = Dict([var => ϵ * var for var in unique(vars)])
20-
subs_expr = Symbolics.expand(substitute_all(subs_expr, rules))
20+
subs_expr = Symbolics.expand(substitute_all(subs_expr, rules; include_derivatives=false))
2121
max_deg = max_power(subs_expr, ϵ)
2222
removal = Dict([ϵ^d => Num(0) for d in deg:max_deg])
23-
res = substitute_all(substitute_all(subs_expr, removal), Dict=> Num(1)))
23+
res = substitute_all(
24+
substitute_all(subs_expr, removal; include_derivatives=false),
25+
Dict=> Num(1));
26+
include_derivatives=false,
27+
)
2428
return Symbolics.expand(res)
2529
end
2630

@@ -30,7 +34,7 @@ end
3034

3135
# calls the above for various types of the first argument
3236
function drop_powers(eq::Equation, var::Vector{Num}, deg::Int)
33-
return drop_powers(eq.lhs, var, deg) .~ drop_powers(eq.lhs, var, deg)
37+
return drop_powers(eq.lhs, var, deg) .~ drop_powers(eq.rhs, var, deg)
3438
end
3539
function drop_powers(eqs::Vector{Equation}, var::Vector{Num}, deg::Int)
3640
return [
@@ -45,9 +49,12 @@ drop_powers(x, vars, deg::Int) = drop_powers(wrap(x), vars, deg)
4549
function max_power(x::Num, y::Num)
4650
terms = get_all_terms(x)
4751
powers = power_of.(terms, y)
48-
literal_powers = Int[
49-
Int(SymbolicUtils.unwrap_const(p)) for p in powers if SymbolicUtils.is_literal_number(p)
50-
]
52+
literal_powers = Int[]
53+
for p in powers
54+
pv = SymbolicUtils.unwrap_const(p)
55+
pv isa Number || continue
56+
push!(literal_powers, Int(pv))
57+
end
5158
isempty(literal_powers) && return 0
5259
return maximum(literal_powers)
5360
end

src/Symbolics/exponentials.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ function simplify_exp_products_mul(expr)
4343
rest_ind = setdiff(1:length(arguments(expr)), ind)
4444
rest = isempty(rest_ind) ? 1 : prod(arguments(expr)[rest_ind])
4545
total = isempty(ind) ? 0 : sum(getindex.(arguments.(arguments(expr)[ind]), 1))
46-
if SymbolicUtils.is_literal_number(total)
47-
return iszero(SymbolicUtils.unwrap_const(total)) ? rest : rest * exp(total)
46+
total_val = SymbolicUtils.unwrap_const(total)
47+
if total_val isa Number
48+
return iszero(total_val) ? rest : rest * exp(total)
4849
end
4950
return rest * exp(total)
5051
end

0 commit comments

Comments
 (0)