diff --git a/src/QuestBase.jl b/src/QuestBase.jl index 8a80959..db05c1b 100644 --- a/src/QuestBase.jl +++ b/src/QuestBase.jl @@ -27,7 +27,6 @@ using Symbolics: Equation, Differential, arguments, - substitute, term, expand, operation, diff --git a/src/Symbolics/Symbolics_utils.jl b/src/Symbolics/Symbolics_utils.jl index 495265f..09ecefb 100644 --- a/src/Symbolics/Symbolics_utils.jl +++ b/src/Symbolics/Symbolics_utils.jl @@ -63,8 +63,26 @@ Perform substitutions in `rules` on `x`. `Symbolics.substitute_in_deriv`. """ Subtype = Union{Num,Equation,BasicSymbolic} + +# SymbolicUtils 4 / Symbolics 7: substitute() does not recurse into call arguments +# (e.g. it leaves x(t) unchanged when substituting t => T). Walk the expression and +# rewrite matching nodes ourselves. +function _deep_substitute(e::BasicSymbolic, unwrap_rules::Dict) + # SU 4's default filter blocks recursion into the arguments of callable-symbolic + # terms (e.g. `x(t)`). Override with an always-true filter so substitutions like + # `t => T` reach inside `x(t)`. Compound keys still match before recursion, and + # SU does not re-walk the replacement, so self-referential rules don't loop. + return SymbolicUtils.substitute(e, unwrap_rules; filterer = _ -> true) +end +_deep_substitute(x::Num, ur::Dict) = wrap(_deep_substitute(unwrap(x), ur)) +function _deep_substitute(eq::Equation, ur::Dict) + return Equation(_deep_substitute(unwrap(eq.lhs), ur), _deep_substitute(unwrap(eq.rhs), ur)) +end +_deep_substitute(x, ::Dict) = x + function substitute_all(x::Subtype, rules::Dict; include_derivatives=true) - result = substitute(x, rules) + unwrap_rules = Dict(unwrap(k) => unwrap(v) for (k, v) in rules) + result = _deep_substitute(x, unwrap_rules) if include_derivatives result = Symbolics.substitute_in_deriv(result, rules) end diff --git a/src/Symbolics/fourier.jl b/src/Symbolics/fourier.jl index 233baa9..c21f0f5 100644 --- a/src/Symbolics/fourier.jl +++ b/src/Symbolics/fourier.jl @@ -37,8 +37,9 @@ is_trig(f::Num) = is_trig(unwrap(f)) is_trig(f) = false function is_trig(f::BasicSymbolic) f = ispow(f) ? arguments(f)[1] : f - isterm(f) && operation(f) ∈ [cos, sin] && return true - return false + isterm(f) || return false + op = operation(f) + return op === cos || op === sin end """ diff --git a/src/utils.jl b/src/utils.jl index fe063ea..76bfc82 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -37,4 +37,8 @@ end is_identity(A::Matrix{Num}) = (@eqsym A == Matrix{Num}(LinearAlgebra.I, size(A)...)) hasnan(x::Matrix{Num}) = any(my_isnan, unwrap.(x)) my_isnan(x) = isnan(x) -my_isnan(x::BasicSymbolic) = false +function my_isnan(x::BasicSymbolic) + SymbolicUtils.isconst(x) || return false + v = Symbolics.value(x) + return v isa Number && isnan(v) +end