From c588a6e75ba0d2ed9ecaf295787c9825928a3483 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Sat, 10 Feb 2024 09:33:20 +0530 Subject: [PATCH 01/18] rename istree to iscall --- Project.toml | 1 + src/SymbolicUtils.jl | 10 ++++++---- src/code.jl | 16 ++++++++-------- src/inspect.jl | 6 +++--- src/interface.jl | 10 +++++----- src/matchers.jl | 4 ++-- src/ordering.jl | 4 ++-- src/polyform.jl | 20 ++++++++++---------- src/rewriters.jl | 10 +++++----- src/rule.jl | 4 ++-- src/simplify.jl | 2 +- src/simplify_rules.jl | 10 +++++----- src/substitute.jl | 4 ++-- src/types.jl | 28 ++++++++++++++-------------- src/utils.jl | 22 +++++++++++----------- test/interface.jl | 2 +- 16 files changed, 78 insertions(+), 75 deletions(-) diff --git a/Project.toml b/Project.toml index dff5c0ac5..1aee7b992 100644 --- a/Project.toml +++ b/Project.toml @@ -22,6 +22,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" Unityper = "a7c27f48-0311-42f6-a7f8-2c11e75eb415" diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index d7e6a6a80..345481c2e 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -4,12 +4,11 @@ $(DocStringExtensions.README) module SymbolicUtils using DocStringExtensions + export @syms, term, showraw, hasmetadata, getmetadata, setmetadata using Unityper - -# Sym, Term, -# Add, Mul and Pow +using TermInterface using DataStructures using Setfield import Setfield: PropertyLens @@ -17,8 +16,11 @@ using SymbolicIndexingInterface import Base: +, -, *, /, //, \, ^, ImmutableDict using ConstructionBase include("interface.jl") + +# Sym, Term, +# Add, Mul and Pow include("types.jl") -export istree, operation, arguments, similarterm +export iscall, operation, arguments, similarterm # Methods on symbolic objects using SpecialFunctions, NaNMath diff --git a/src/code.jl b/src/code.jl index 7c647ba9c..c262fc8ed 100644 --- a/src/code.jl +++ b/src/code.jl @@ -8,7 +8,7 @@ export toexpr, Assignment, (←), Let, Func, DestructuredArgs, LiteralExpr, import ..SymbolicUtils import ..SymbolicUtils.Rewriters -import SymbolicUtils: @matchable, BasicSymbolic, Sym, Term, istree, operation, arguments, issym, +import SymbolicUtils: @matchable, BasicSymbolic, Sym, Term, iscall, operation, arguments, issym, symtype, similarterm, unsorted_arguments, metadata, isterm, term ##== state management ==## @@ -162,7 +162,7 @@ end toexpr(O::Expr, st) = O function substitute_name(O, st) - if (issym(O) || istree(O)) && haskey(st.rewrites, O) + if (issym(O) || iscall(O)) && haskey(st.rewrites, O) st.rewrites[O] else O @@ -176,13 +176,13 @@ function toexpr(O, st) end O = substitute_name(O, st) - !istree(O) && return O + !iscall(O) && return O op = operation(O) expr′ = function_to_expr(op, O, st) if expr′ !== nothing return expr′ else - !istree(O) && return O + !iscall(O) && return O args = arguments(O) return Expr(:call, toexpr(op, st), map(x->toexpr(x, st), args)...) end @@ -221,7 +221,7 @@ get_rewrites(args::DestructuredArgs) = () function get_rewrites(args::Union{AbstractArray, Tuple}) cflatten(map(get_rewrites, args)) end -get_rewrites(x) = istree(x) ? (x,) : () +get_rewrites(x) = iscall(x) ? (x,) : () cflatten(x) = Iterators.flatten(x) |> collect # Used in Symbolics @@ -691,7 +691,7 @@ end @inline newsym(::Type{T}) where T = Sym{T}(gensym("cse")) function _cse!(mem, expr) - istree(expr) || return expr + iscall(expr) || return expr op = _cse!(mem, operation(expr)) args = map(Base.Fix1(_cse!, mem), arguments(expr)) t = similarterm(expr, op, args) @@ -742,7 +742,7 @@ end function cse_state!(state, t) - !istree(t) && return t + !iscall(t) && return t state[t] = Base.get!(state, t, 0) + 1 foreach(x->cse_state!(state, x), unsorted_arguments(t)) end @@ -758,7 +758,7 @@ function cse_block!(assignments, counter, names, name, state, x) counter[] += 1 return sym end - elseif istree(x) + elseif iscall(x) args = map(a->cse_block!(assignments, counter, names, name, state,a), unsorted_arguments(x)) if isterm(x) return term(operation(x), args...) diff --git a/src/inspect.jl b/src/inspect.jl index f62551893..5060894fc 100644 --- a/src/inspect.jl +++ b/src/inspect.jl @@ -2,11 +2,11 @@ import AbstractTrees const inspect_metadata = Ref{Bool}(false) function AbstractTrees.nodevalue(x::Symbolic) - istree(x) ? operation(x) : x + iscall(x) ? operation(x) : x end function AbstractTrees.nodevalue(x::BasicSymbolic) - str = if !istree(x) + str = if !iscall(x) string(exprtype(x), "(", x, ")") elseif isadd(x) string(exprtype(x), @@ -27,7 +27,7 @@ function AbstractTrees.nodevalue(x::BasicSymbolic) end function AbstractTrees.children(x::Symbolic) - istree(x) ? arguments(x) : () + iscall(x) ? arguments(x) : () end """ diff --git a/src/interface.jl b/src/interface.jl index 255ef584f..687a802a8 100644 --- a/src/interface.jl +++ b/src/interface.jl @@ -1,10 +1,10 @@ """ - istree(x) + iscall(x) Returns `true` if `x` is a term. If true, `operation`, `arguments` must also be defined for `x` appropriately. """ -istree(x) = false +iscall(x) = false """ symtype(x) @@ -29,7 +29,7 @@ issym(x) = false """ operation(x) -If `x` is a term as defined by `istree(x)`, `operation(x)` returns the +If `x` is a term as defined by `iscall(x)`, `operation(x)` returns the head of the term if `x` represents a function call, for example, the head is the function being called. """ @@ -38,14 +38,14 @@ function operation end """ arguments(x) -Get the arguments of `x`, must be defined if `istree(x)` is `true`. +Get the arguments of `x`, must be defined if `iscall(x)` is `true`. """ function arguments end """ unsorted_arguments(x::T) -If x is a term satisfying `istree(x)` and your term type `T` provides +If x is a term satisfying `iscall(x)` and your term type `T` provides an optimized implementation for storing the arguments, this function can be used to retrieve the arguments when the order of arguments does not matter but the speed of the operation does. diff --git a/src/matchers.jl b/src/matchers.jl index 531bc1535..7f4dea537 100644 --- a/src/matchers.jl +++ b/src/matchers.jl @@ -6,7 +6,7 @@ # 3. Callback: takes arguments Dictionary × Number of elements matched # function matcher(val::Any) - istree(val) && return term_matcher(val) + iscall(val) && return term_matcher(val) function literal_matcher(next, data, bindings) islist(data) && isequal(car(data), val) ? next(bindings, 1) : nothing end @@ -89,7 +89,7 @@ function term_matcher(term) function term_matcher(success, data, bindings) !islist(data) && return nothing - !istree(car(data)) && return nothing + !iscall(car(data)) && return nothing function loop(term, bindings′, matchers′) # Get it to compile faster if !islist(matchers′) diff --git a/src/ordering.jl b/src/ordering.jl index 81d64a9b4..3417f3f85 100644 --- a/src/ordering.jl +++ b/src/ordering.jl @@ -20,7 +20,7 @@ function get_degrees(expr) if issym(expr) ((Symbol(expr),) => 1,) - elseif istree(expr) + elseif iscall(expr) op = operation(expr) args = arguments(expr) if operation(expr) == (^) && args[2] isa Number @@ -62,7 +62,7 @@ function lexlt(degs1, degs2) return false # they are equal end -_arglen(a) = istree(a) ? length(unsorted_arguments(a)) : 0 +_arglen(a) = iscall(a) ? length(unsorted_arguments(a)) : 0 function <ₑ(a::Tuple, b::Tuple) for (x, y) in zip(a, b) diff --git a/src/polyform.jl b/src/polyform.jl index 873e332a8..360a85351 100644 --- a/src/polyform.jl +++ b/src/polyform.jl @@ -6,7 +6,7 @@ using Bijections Abstracts a [MultivariatePolynomials.jl](https://juliaalgebra.github.io/MultivariatePolynomials.jl/stable/) as a SymbolicUtils expression and vice-versa. -The SymbolicUtils term interface (`istree`, `operation, and `arguments`) works on PolyForm lazily: +The SymbolicUtils term interface (`iscall`, `operation, and `arguments`) works on PolyForm lazily: the `operation` and `arguments` are created by converting one level of arguments into SymbolicUtils expressions. They may further contain PolyForm within them. We use this to hold polynomials in memory while doing `simplify_fractions`. @@ -97,7 +97,7 @@ _isone(p::PolyForm) = isone(p.p) function polyize(x, pvar2sym, sym2term, vtype, pow, Fs, recurse) if x isa Number return x - elseif istree(x) + elseif iscall(x) if !(symtype(x) <: Number) error("Cannot convert $x of symtype $(symtype(x)) into a PolyForm") end @@ -170,8 +170,8 @@ function PolyForm(x, PolyForm{symtype(x)}(p, pvar2sym, sym2term, metadata) end -istree(x::Type{<:PolyForm}) = true -istree(x::PolyForm) = true +iscall(x::Type{<:PolyForm}) = true +iscall(x::PolyForm) = true function similarterm(t::PolyForm, f, args, symtype; metadata=nothing) basic_similarterm(t, f, args, symtype; metadata=metadata) @@ -336,7 +336,7 @@ function simplify_fractions(x; polyform=false) end function add_with_div(x, flatten=true) - (!istree(x) || operation(x) != (+)) && return x + (!iscall(x) || operation(x) != (+)) && return x aa = unsorted_arguments(x) !any(a->isdiv(a), aa) && return x # no rewrite necessary @@ -361,7 +361,7 @@ function flatten_fractions(x) end function fraction_iszero(x) - !istree(x) && return _iszero(x) + !iscall(x) && return _iszero(x) ff = flatten_fractions(x) # fast path and then slow path any(_iszero, numerators(ff)) || @@ -369,18 +369,18 @@ function fraction_iszero(x) end function fraction_isone(x) - !istree(x) && return _isone(x) + !iscall(x) && return _isone(x) _isone(simplify_fractions(flatten_fractions(x))) end function needs_div_rules(x) (isdiv(x) && !(x.num isa Number) && !(x.den isa Number)) || - (istree(x) && operation(x) === (+) && count(has_div, unsorted_arguments(x)) > 1) || - (istree(x) && any(needs_div_rules, unsorted_arguments(x))) + (iscall(x) && operation(x) === (+) && count(has_div, unsorted_arguments(x)) > 1) || + (iscall(x) && any(needs_div_rules, unsorted_arguments(x))) end function has_div(x) - return isdiv(x) || (istree(x) && any(has_div, unsorted_arguments(x))) + return isdiv(x) || (iscall(x) && any(has_div, unsorted_arguments(x))) end flatten_pows(xs) = map(xs) do x diff --git a/src/rewriters.jl b/src/rewriters.jl index fffd3ee95..e894747cc 100644 --- a/src/rewriters.jl +++ b/src/rewriters.jl @@ -32,7 +32,7 @@ rewriters. module Rewriters using SymbolicUtils: @timer -import SymbolicUtils: similarterm, istree, operation, arguments, unsorted_arguments, node_count +import SymbolicUtils: similarterm, iscall, operation, arguments, unsorted_arguments, node_count export Empty, IfElse, If, Chain, RestartedChain, Fixpoint, Postwalk, Prewalk, PassThrough # Cache of printed rules to speed up @timer @@ -190,11 +190,11 @@ instrument(x::PassThrough, f) = PassThrough(instrument(x.rw, f)) passthrough(x, default) = x === nothing ? default : x function (p::Walk{ord, C, F, false})(x) where {ord, C, F} @assert ord === :pre || ord === :post - if istree(x) + if iscall(x) if ord === :pre x = p.rw(x) end - if istree(x) + if iscall(x) x = p.similarterm(x, operation(x), map(PassThrough(p), unsorted_arguments(x))) end return ord === :post ? p.rw(x) : x @@ -205,11 +205,11 @@ end function (p::Walk{ord, C, F, true})(x) where {ord, C, F} @assert ord === :pre || ord === :post - if istree(x) + if iscall(x) if ord === :pre x = p.rw(x) end - if istree(x) + if iscall(x) _args = map(arguments(x)) do arg if node_count(arg) > p.thread_cutoff Threads.@spawn p(arg) diff --git a/src/rule.jl b/src/rule.jl index a872222e6..a529979e7 100644 --- a/src/rule.jl +++ b/src/rule.jl @@ -120,7 +120,7 @@ end getdepth(r::Rule) = r.depth function rule_depth(rule, d=0, maxdepth=0) - if istree(rule) + if iscall(rule) maxdepth = reduce(max, (rule_depth(r, d+1, maxdepth) for r in arguments(rule)), init=1) elseif rule isa Slot || rule isa Segment maxdepth = max(d, maxdepth) @@ -389,7 +389,7 @@ Base.show(io::IO, acr::ACRule) = print(io, "ACRule(", acr.rule, ")") function (acr::ACRule)(term) r = Rule(acr) - if !istree(term) + if !iscall(term) r(term) else f = operation(term) diff --git a/src/simplify.jl b/src/simplify.jl index 0c0bfd44a..87bc95954 100644 --- a/src/simplify.jl +++ b/src/simplify.jl @@ -43,7 +43,7 @@ function simplify(x; SymbolicUtils.simplify_fractions(x) : x end -has_operation(x, op) = (istree(x) && (operation(x) == op || +has_operation(x, op) = (iscall(x) && (operation(x) == op || any(a->has_operation(a, op), unsorted_arguments(x)))) diff --git a/src/simplify_rules.jl b/src/simplify_rules.jl index b12a9f272..a612036cb 100644 --- a/src/simplify_rules.jl +++ b/src/simplify_rules.jl @@ -2,9 +2,9 @@ using .Rewriters """ is_operation(f) Returns a single argument anonymous function predicate, that returns `true` if and only if -the argument to the predicate satisfies `istree` and `operation(x) == f` +the argument to the predicate satisfies `iscall` and `operation(x) == f` """ -is_operation(f) = @nospecialize(x) -> istree(x) && (operation(x) == f) +is_operation(f) = @nospecialize(x) -> iscall(x) && (operation(x) == f) let CANONICALIZE_PLUS = [ @@ -132,7 +132,7 @@ let ] function number_simplifier() - rule_tree = [If(istree, Chain(ASSORTED_RULES)), + rule_tree = [If(iscall, Chain(ASSORTED_RULES)), If(x -> !isadd(x) && is_operation(+)(x), Chain(CANONICALIZE_PLUS)), If(is_operation(+), Chain(PLUS_DISTRIBUTE)), # This would be useful even if isadd @@ -173,12 +173,12 @@ let end # reduce overhead of simplify by defining these as constant - serial_simplifier = If(istree, Fixpoint(default_simplifier())) + serial_simplifier = If(iscall, Fixpoint(default_simplifier())) threaded_simplifier(cutoff) = Fixpoint(default_simplifier(threaded=true, thread_cutoff=cutoff)) - serial_expand_simplifier = If(istree, + serial_expand_simplifier = If(iscall, Fixpoint(Chain((expand, Fixpoint(default_simplifier()))))) diff --git a/src/substitute.jl b/src/substitute.jl index 9a5213f0b..73ea7659d 100644 --- a/src/substitute.jl +++ b/src/substitute.jl @@ -16,7 +16,7 @@ julia> substitute(1+sqrt(y), Dict(y => 2), fold=false) function substitute(expr, dict; fold=true) haskey(dict, expr) && return dict[expr] - if istree(expr) + if iscall(expr) op = substitute(operation(expr), dict; fold=fold) if fold canfold = !(op isa Symbolic) @@ -53,7 +53,7 @@ Base.occursin(needle::Symbolic, haystack) = _occursin(needle, haystack) function _occursin(needle, haystack) isequal(needle, haystack) && return true - if istree(haystack) + if iscall(haystack) args = arguments(haystack) for arg in args occursin(needle, arg) && return true diff --git a/src/types.jl b/src/types.jl index 840f8a6e1..452eeb18c 100644 --- a/src/types.jl +++ b/src/types.jl @@ -183,7 +183,7 @@ function unsorted_arguments(x::BasicSymbolic) return args end -istree(s::BasicSymbolic) = !issym(s) +iscall(s::BasicSymbolic) = !issym(s) @inline isa_SymType(T::Val{S}, x) where {S} = x isa BasicSymbolic ? Unityper.isa_type_fun(Val(SymbolicUtils.BasicSymbolic), T, x) : false issym(x::BasicSymbolic) = isa_SymType(Val(:Sym), x) isterm(x) = isa_SymType(Val(:Term), x) @@ -400,7 +400,7 @@ end @inline function numerators(x) isdiv(x) && return numerators(x.num) - istree(x) && operation(x) === (*) ? arguments(x) : Any[x] + iscall(x) && operation(x) === (*) ? arguments(x) : Any[x] end @inline denominators(x) = isdiv(x) ? numerators(x.den) : Any[1] @@ -516,7 +516,7 @@ end Binarizes `Term`s with n-ary operations """ function unflatten(t::Symbolic{T}) where{T} - if istree(t) + if iscall(t) f = operation(t) if f == (+) || f == (*) # TODO check out for other n-ary --> binary ops a = arguments(t) @@ -587,9 +587,9 @@ function issafecanon(f, s) _issafecanon(f, s) end end -_issafecanon(::typeof(*), s) = !istree(s) || !(operation(s) in (+,*,^)) -_issafecanon(::typeof(+), s) = !istree(s) || !(operation(s) in (+,*)) -_issafecanon(::typeof(^), s) = !istree(s) || !(operation(s) in (*, ^)) +_issafecanon(::typeof(*), s) = !iscall(s) || !(operation(s) in (+,*,^)) +_issafecanon(::typeof(+), s) = !iscall(s) || !(operation(s) in (+,*)) +_issafecanon(::typeof(^), s) = !iscall(s) || !(operation(s) in (*, ^)) issafecanon(f, ss...) = all(x->issafecanon(f, x), ss) @@ -641,7 +641,7 @@ end function to_symbolic(x) Base.depwarn("`to_symbolic(x)` is deprecated, define the interface for your " * - "symbolic structure using `istree(x)`, `operation(x)`, `arguments(x)` " * + "symbolic structure using `iscall(x)`, `operation(x)`, `arguments(x)` " * "and `similarterm(::YourType, f, args, symtype)`", :to_symbolic, force=true) x @@ -654,7 +654,7 @@ const show_simplified = Ref(false) isnegative(t::Real) = t < 0 function isnegative(t) - if istree(t) && operation(t) === (*) + if iscall(t) && operation(t) === (*) coeff = first(arguments(t)) return isnegative(coeff) end @@ -666,7 +666,7 @@ setargs(t, args) = Term{symtype(t)}(operation(t), args) cdrargs(args) = setargs(t, cdr(args)) print_arg(io, x::Union{Complex, Rational}; paren=true) = print(io, "(", x, ")") -isbinop(f) = istree(f) && !istree(operation(f)) && Base.isbinaryoperator(nameof(operation(f))) +isbinop(f) = iscall(f) && !iscall(operation(f)) && Base.isbinaryoperator(nameof(operation(f))) function print_arg(io, x; paren=false) if paren && isbinop(x) print(io, "(", x, ")") @@ -685,7 +685,7 @@ function print_arg(io, f, x) end function remove_minus(t) - !istree(t) && return -t + !iscall(t) && return -t @assert operation(t) == (*) args = arguments(t) @assert args[1] < 0 @@ -756,9 +756,9 @@ function show_ref(io, f, args) x = args[1] idx = args[2:end] - istree(x) && print(io, "(") + iscall(x) && print(io, "(") print(io, x) - istree(x) && print(io, ")") + iscall(x) && print(io, ")") print(io, "[") for i=1:length(idx) print_arg(io, idx[i]) @@ -768,7 +768,7 @@ function show_ref(io, f, args) end function show_call(io, f, args) - fname = istree(f) ? Symbol(repr(f)) : nameof(f) + fname = iscall(f) ? Symbol(repr(f)) : nameof(f) len_args = length(args) if Base.isunaryoperator(fname) && len_args == 1 print(io, "$fname") @@ -810,7 +810,7 @@ function show_term(io::IO, t) show_pow(io, args) elseif f === (getindex) show_ref(io, f, args) - elseif f === (identity) && !issym(args[1]) && !istree(args[1]) + elseif f === (identity) && !issym(args[1]) && !iscall(args[1]) show(io, args[1]) else show_call(io, f, args) diff --git a/src/utils.jl b/src/utils.jl index acf9e92d6..8e875c6e6 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -35,7 +35,7 @@ pow(x::Symbolic,y::Symbolic) = Base.:^(x,y) # Simplification utilities function has_trig_exp(term) - !istree(term) && return false + !iscall(term) && return false fns = (sin, cos, tan, cot, sec, csc, exp, cosh, sinh) op = operation(term) @@ -47,7 +47,7 @@ function has_trig_exp(term) end function fold(t) - if istree(t) + if iscall(t) tt = map(fold, arguments(t)) if !any(x->x isa Symbolic, tt) # evaluate it @@ -81,7 +81,7 @@ function isnotflat(⋆) function (x) args = arguments(x) for t in args - if istree(t) && operation(t) === (⋆) + if iscall(t) && operation(t) === (⋆) return true end end @@ -141,7 +141,7 @@ function flatten_term(⋆, x) # flatten nested ⋆ flattened_args = [] for t in args - if istree(t) && operation(t) === (⋆) + if iscall(t) && operation(t) === (⋆) append!(flattened_args, arguments(t)) else push!(flattened_args, t) @@ -170,7 +170,7 @@ struct LL{V} i::Int end -islist(x) = istree(x) || !isempty(x) +islist(x) = iscall(x) || !isempty(x) Base.empty(l::LL) = empty(l.v) Base.isempty(l::LL) = l.i > length(l.v) @@ -184,9 +184,9 @@ Base.isempty(t::Term) = false @inline car(t::Term) = operation(t) @inline cdr(t::Term) = arguments(t) -@inline car(v) = istree(v) ? operation(v) : first(v) +@inline car(v) = iscall(v) ? operation(v) : first(v) @inline function cdr(v) - if istree(v) + if iscall(v) arguments(v) else islist(v) ? LL(v, 2) : error("asked cdr of empty") @@ -200,7 +200,7 @@ end if n === 0 return ll else - istree(ll) ? drop_n(arguments(ll), n-1) : drop_n(cdr(ll), n-1) + iscall(ll) ? drop_n(arguments(ll), n-1) : drop_n(cdr(ll), n-1) end end @inline drop_n(ll::Union{Tuple, AbstractArray}, n) = drop_n(LL(ll, 1), n) @@ -219,7 +219,7 @@ macro matchable(expr) fields = map(get_name, fields) quote $expr - SymbolicUtils.istree(::$name) = true + SymbolicUtils.iscall(::$name) = true SymbolicUtils.operation(::$name) = $name SymbolicUtils.arguments(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) Base.length(x::$name) = $(length(fields) + 1) @@ -229,7 +229,7 @@ end """ node_count(t) -Count the nodes in a symbolic expression tree satisfying `istree` and `arguments`. +Count the nodes in a symbolic expression tree satisfying `iscall` and `arguments`. """ -node_count(t) = istree(t) ? reduce(+, node_count(x) for x in arguments(t), init = 0) + 1 : 1 +node_count(t) = iscall(t) ? reduce(+, node_count(x) for x in arguments(t), init = 0) + 1 : 1 diff --git a/test/interface.jl b/test/interface.jl index d98d97328..af83fc89f 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,5 +1,5 @@ using SymbolicUtils, Test -import SymbolicUtils: istree, issym, operation, arguments, symtype +import SymbolicUtils: iscall, issym, operation, arguments, symtype issym(s::Symbol) = true Base.nameof(s::Symbol) = s From 4e5a5c492f93eb0c47591262cf04563b70188312 Mon Sep 17 00:00:00 2001 From: a Date: Sun, 10 Mar 2024 19:16:04 +0100 Subject: [PATCH 02/18] re-start integration --- Project.toml | 1 + docs/src/api.md | 9 --------- docs/src/index.md | 11 ++--------- src/SymbolicUtils.jl | 7 +++++-- src/code.jl | 16 ++++++++-------- src/inspect.jl | 6 +++--- src/matchers.jl | 4 ++-- src/ordering.jl | 4 ++-- src/polyform.jl | 26 +++++++++++++++----------- src/rewriters.jl | 11 ++++++----- src/rule.jl | 4 ++-- src/simplify.jl | 2 +- src/simplify_rules.jl | 10 +++++----- src/substitute.jl | 4 ++-- src/types.jl | 37 ++++++++++++++++++++----------------- src/utils.jl | 22 +++++++++++----------- test/interface.jl | 2 +- 17 files changed, 86 insertions(+), 90 deletions(-) diff --git a/Project.toml b/Project.toml index dff5c0ac5..1aee7b992 100644 --- a/Project.toml +++ b/Project.toml @@ -22,6 +22,7 @@ SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5" +TermInterface = "8ea1fca8-c5ef-4a55-8b96-4e9afe9c9a3c" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" Unityper = "a7c27f48-0311-42f6-a7f8-2c11e75eb415" diff --git a/docs/src/api.md b/docs/src/api.md index eb93f3ee0..45266df82 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -12,15 +12,6 @@ SymbolicUtils.Pow SymbolicUtils.promote_symtype ``` -## Interfacing - -```@docs -SymbolicUtils.istree -SymbolicUtils.operation -SymbolicUtils.arguments -SymbolicUtils.similarterm -``` - ## Rewriters ```@docs diff --git a/docs/src/index.md b/docs/src/index.md index f7cc0a7f7..50829f591 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -108,15 +108,8 @@ g(2//5, g(1, β)) Symbolic expressions are of type `Term{T}`, `Add{T}`, `Mul{T}`, `Pow{T}` or `Div{T}` and denote some function call where one or more arguments are themselves such expressions or `Sym`s. See more about the representation [here](/representation/). -All the expression types support the following: - -- `istree(x)` -- always returns `true` denoting, `x` is not a leaf node like Sym or a literal. -- `operation(x)` -- the function being called -- `arguments(x)` -- a vector of arguments -- `symtype(x)` -- the "inferred" type (`T`) - -See more on the interface [here](/interface) - +All the expression types support the [TermInterface.jl](https://github.com/0x0f0f0f/TermInterface.jl) interface. +Please refer to the package for the complete reference of the interface. ## Term rewriting diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index d7e6a6a80..cf34a0563 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -16,9 +16,12 @@ import Setfield: PropertyLens using SymbolicIndexingInterface import Base: +, -, *, /, //, \, ^, ImmutableDict using ConstructionBase -include("interface.jl") +using TermInterface +import TermInterface: iscall, isexpr, issym, symtype, head, children, operation, arguments + +function similarterm end include("types.jl") -export istree, operation, arguments, similarterm +export similarterm # Methods on symbolic objects using SpecialFunctions, NaNMath diff --git a/src/code.jl b/src/code.jl index 7c647ba9c..c262fc8ed 100644 --- a/src/code.jl +++ b/src/code.jl @@ -8,7 +8,7 @@ export toexpr, Assignment, (←), Let, Func, DestructuredArgs, LiteralExpr, import ..SymbolicUtils import ..SymbolicUtils.Rewriters -import SymbolicUtils: @matchable, BasicSymbolic, Sym, Term, istree, operation, arguments, issym, +import SymbolicUtils: @matchable, BasicSymbolic, Sym, Term, iscall, operation, arguments, issym, symtype, similarterm, unsorted_arguments, metadata, isterm, term ##== state management ==## @@ -162,7 +162,7 @@ end toexpr(O::Expr, st) = O function substitute_name(O, st) - if (issym(O) || istree(O)) && haskey(st.rewrites, O) + if (issym(O) || iscall(O)) && haskey(st.rewrites, O) st.rewrites[O] else O @@ -176,13 +176,13 @@ function toexpr(O, st) end O = substitute_name(O, st) - !istree(O) && return O + !iscall(O) && return O op = operation(O) expr′ = function_to_expr(op, O, st) if expr′ !== nothing return expr′ else - !istree(O) && return O + !iscall(O) && return O args = arguments(O) return Expr(:call, toexpr(op, st), map(x->toexpr(x, st), args)...) end @@ -221,7 +221,7 @@ get_rewrites(args::DestructuredArgs) = () function get_rewrites(args::Union{AbstractArray, Tuple}) cflatten(map(get_rewrites, args)) end -get_rewrites(x) = istree(x) ? (x,) : () +get_rewrites(x) = iscall(x) ? (x,) : () cflatten(x) = Iterators.flatten(x) |> collect # Used in Symbolics @@ -691,7 +691,7 @@ end @inline newsym(::Type{T}) where T = Sym{T}(gensym("cse")) function _cse!(mem, expr) - istree(expr) || return expr + iscall(expr) || return expr op = _cse!(mem, operation(expr)) args = map(Base.Fix1(_cse!, mem), arguments(expr)) t = similarterm(expr, op, args) @@ -742,7 +742,7 @@ end function cse_state!(state, t) - !istree(t) && return t + !iscall(t) && return t state[t] = Base.get!(state, t, 0) + 1 foreach(x->cse_state!(state, x), unsorted_arguments(t)) end @@ -758,7 +758,7 @@ function cse_block!(assignments, counter, names, name, state, x) counter[] += 1 return sym end - elseif istree(x) + elseif iscall(x) args = map(a->cse_block!(assignments, counter, names, name, state,a), unsorted_arguments(x)) if isterm(x) return term(operation(x), args...) diff --git a/src/inspect.jl b/src/inspect.jl index f62551893..ca1970dd5 100644 --- a/src/inspect.jl +++ b/src/inspect.jl @@ -2,11 +2,11 @@ import AbstractTrees const inspect_metadata = Ref{Bool}(false) function AbstractTrees.nodevalue(x::Symbolic) - istree(x) ? operation(x) : x + iscall(x) ? operation(x) : x end function AbstractTrees.nodevalue(x::BasicSymbolic) - str = if !istree(x) + str = if !iscall(x) string(exprtype(x), "(", x, ")") elseif isadd(x) string(exprtype(x), @@ -27,7 +27,7 @@ function AbstractTrees.nodevalue(x::BasicSymbolic) end function AbstractTrees.children(x::Symbolic) - istree(x) ? arguments(x) : () + isexpr(x) ? children(x) : () end """ diff --git a/src/matchers.jl b/src/matchers.jl index 531bc1535..afa95d8d0 100644 --- a/src/matchers.jl +++ b/src/matchers.jl @@ -6,7 +6,7 @@ # 3. Callback: takes arguments Dictionary × Number of elements matched # function matcher(val::Any) - istree(val) && return term_matcher(val) + isexpr(val) && return term_matcher(val) function literal_matcher(next, data, bindings) islist(data) && isequal(car(data), val) ? next(bindings, 1) : nothing end @@ -89,7 +89,7 @@ function term_matcher(term) function term_matcher(success, data, bindings) !islist(data) && return nothing - !istree(car(data)) && return nothing + !iscall(car(data)) && return nothing function loop(term, bindings′, matchers′) # Get it to compile faster if !islist(matchers′) diff --git a/src/ordering.jl b/src/ordering.jl index 81d64a9b4..3417f3f85 100644 --- a/src/ordering.jl +++ b/src/ordering.jl @@ -20,7 +20,7 @@ function get_degrees(expr) if issym(expr) ((Symbol(expr),) => 1,) - elseif istree(expr) + elseif iscall(expr) op = operation(expr) args = arguments(expr) if operation(expr) == (^) && args[2] isa Number @@ -62,7 +62,7 @@ function lexlt(degs1, degs2) return false # they are equal end -_arglen(a) = istree(a) ? length(unsorted_arguments(a)) : 0 +_arglen(a) = iscall(a) ? length(unsorted_arguments(a)) : 0 function <ₑ(a::Tuple, b::Tuple) for (x, y) in zip(a, b) diff --git a/src/polyform.jl b/src/polyform.jl index 873e332a8..8d9474cd5 100644 --- a/src/polyform.jl +++ b/src/polyform.jl @@ -6,7 +6,7 @@ using Bijections Abstracts a [MultivariatePolynomials.jl](https://juliaalgebra.github.io/MultivariatePolynomials.jl/stable/) as a SymbolicUtils expression and vice-versa. -The SymbolicUtils term interface (`istree`, `operation, and `arguments`) works on PolyForm lazily: +The SymbolicUtils term interface (`isexpr`, `operation, and `arguments`) works on PolyForm lazily: the `operation` and `arguments` are created by converting one level of arguments into SymbolicUtils expressions. They may further contain PolyForm within them. We use this to hold polynomials in memory while doing `simplify_fractions`. @@ -97,7 +97,7 @@ _isone(p::PolyForm) = isone(p.p) function polyize(x, pvar2sym, sym2term, vtype, pow, Fs, recurse) if x isa Number return x - elseif istree(x) + elseif iscall(x) if !(symtype(x) <: Number) error("Cannot convert $x of symtype $(symtype(x)) into a PolyForm") end @@ -170,8 +170,11 @@ function PolyForm(x, PolyForm{symtype(x)}(p, pvar2sym, sym2term, metadata) end -istree(x::Type{<:PolyForm}) = true -istree(x::PolyForm) = true +isexpr(x::Type{<:PolyForm}) = true +isexpr(x::PolyForm) = true +iscall(x::Type{<:PolyForm}) = true +iscall(x::PolyForm) = true + function similarterm(t::PolyForm, f, args, symtype; metadata=nothing) basic_similarterm(t, f, args, symtype; metadata=metadata) @@ -181,7 +184,8 @@ function similarterm(::PolyForm, f::Union{typeof(*), typeof(+), typeof(^)}, f(args...) end -operation(x::PolyForm) = MP.nterms(x.p) == 1 ? (*) : (+) +head(x::PolyForm) = MP.nterms(x.p) == 1 ? (*) : (+) +operation(x::PolyForm) = head(x) function arguments(x::PolyForm{T}) where {T} @@ -336,7 +340,7 @@ function simplify_fractions(x; polyform=false) end function add_with_div(x, flatten=true) - (!istree(x) || operation(x) != (+)) && return x + (!iscall(x) || operation(x) != (+)) && return x aa = unsorted_arguments(x) !any(a->isdiv(a), aa) && return x # no rewrite necessary @@ -361,7 +365,7 @@ function flatten_fractions(x) end function fraction_iszero(x) - !istree(x) && return _iszero(x) + !iscall(x) && return _iszero(x) ff = flatten_fractions(x) # fast path and then slow path any(_iszero, numerators(ff)) || @@ -369,18 +373,18 @@ function fraction_iszero(x) end function fraction_isone(x) - !istree(x) && return _isone(x) + !iscall(x) && return _isone(x) _isone(simplify_fractions(flatten_fractions(x))) end function needs_div_rules(x) (isdiv(x) && !(x.num isa Number) && !(x.den isa Number)) || - (istree(x) && operation(x) === (+) && count(has_div, unsorted_arguments(x)) > 1) || - (istree(x) && any(needs_div_rules, unsorted_arguments(x))) + (iscall(x) && operation(x) === (+) && count(has_div, unsorted_arguments(x)) > 1) || + (iscall(x) && any(needs_div_rules, unsorted_arguments(x))) end function has_div(x) - return isdiv(x) || (istree(x) && any(has_div, unsorted_arguments(x))) + return isdiv(x) || (iscall(x) && any(has_div, unsorted_arguments(x))) end flatten_pows(xs) = map(xs) do x diff --git a/src/rewriters.jl b/src/rewriters.jl index fffd3ee95..464898d36 100644 --- a/src/rewriters.jl +++ b/src/rewriters.jl @@ -31,8 +31,9 @@ rewriters. """ module Rewriters using SymbolicUtils: @timer +using TermInterface -import SymbolicUtils: similarterm, istree, operation, arguments, unsorted_arguments, node_count +import SymbolicUtils: similarterm export Empty, IfElse, If, Chain, RestartedChain, Fixpoint, Postwalk, Prewalk, PassThrough # Cache of printed rules to speed up @timer @@ -190,11 +191,11 @@ instrument(x::PassThrough, f) = PassThrough(instrument(x.rw, f)) passthrough(x, default) = x === nothing ? default : x function (p::Walk{ord, C, F, false})(x) where {ord, C, F} @assert ord === :pre || ord === :post - if istree(x) + if iscall(x) if ord === :pre x = p.rw(x) end - if istree(x) + if iscall(x) x = p.similarterm(x, operation(x), map(PassThrough(p), unsorted_arguments(x))) end return ord === :post ? p.rw(x) : x @@ -205,11 +206,11 @@ end function (p::Walk{ord, C, F, true})(x) where {ord, C, F} @assert ord === :pre || ord === :post - if istree(x) + if iscall(x) if ord === :pre x = p.rw(x) end - if istree(x) + if iscall(x) _args = map(arguments(x)) do arg if node_count(arg) > p.thread_cutoff Threads.@spawn p(arg) diff --git a/src/rule.jl b/src/rule.jl index a872222e6..cc71ecaaa 100644 --- a/src/rule.jl +++ b/src/rule.jl @@ -120,7 +120,7 @@ end getdepth(r::Rule) = r.depth function rule_depth(rule, d=0, maxdepth=0) - if istree(rule) + if isexpr(rule) maxdepth = reduce(max, (rule_depth(r, d+1, maxdepth) for r in arguments(rule)), init=1) elseif rule isa Slot || rule isa Segment maxdepth = max(d, maxdepth) @@ -389,7 +389,7 @@ Base.show(io::IO, acr::ACRule) = print(io, "ACRule(", acr.rule, ")") function (acr::ACRule)(term) r = Rule(acr) - if !istree(term) + if !isexpr(term) r(term) else f = operation(term) diff --git a/src/simplify.jl b/src/simplify.jl index 0c0bfd44a..87bc95954 100644 --- a/src/simplify.jl +++ b/src/simplify.jl @@ -43,7 +43,7 @@ function simplify(x; SymbolicUtils.simplify_fractions(x) : x end -has_operation(x, op) = (istree(x) && (operation(x) == op || +has_operation(x, op) = (iscall(x) && (operation(x) == op || any(a->has_operation(a, op), unsorted_arguments(x)))) diff --git a/src/simplify_rules.jl b/src/simplify_rules.jl index b12a9f272..a612036cb 100644 --- a/src/simplify_rules.jl +++ b/src/simplify_rules.jl @@ -2,9 +2,9 @@ using .Rewriters """ is_operation(f) Returns a single argument anonymous function predicate, that returns `true` if and only if -the argument to the predicate satisfies `istree` and `operation(x) == f` +the argument to the predicate satisfies `iscall` and `operation(x) == f` """ -is_operation(f) = @nospecialize(x) -> istree(x) && (operation(x) == f) +is_operation(f) = @nospecialize(x) -> iscall(x) && (operation(x) == f) let CANONICALIZE_PLUS = [ @@ -132,7 +132,7 @@ let ] function number_simplifier() - rule_tree = [If(istree, Chain(ASSORTED_RULES)), + rule_tree = [If(iscall, Chain(ASSORTED_RULES)), If(x -> !isadd(x) && is_operation(+)(x), Chain(CANONICALIZE_PLUS)), If(is_operation(+), Chain(PLUS_DISTRIBUTE)), # This would be useful even if isadd @@ -173,12 +173,12 @@ let end # reduce overhead of simplify by defining these as constant - serial_simplifier = If(istree, Fixpoint(default_simplifier())) + serial_simplifier = If(iscall, Fixpoint(default_simplifier())) threaded_simplifier(cutoff) = Fixpoint(default_simplifier(threaded=true, thread_cutoff=cutoff)) - serial_expand_simplifier = If(istree, + serial_expand_simplifier = If(iscall, Fixpoint(Chain((expand, Fixpoint(default_simplifier()))))) diff --git a/src/substitute.jl b/src/substitute.jl index 9a5213f0b..73ea7659d 100644 --- a/src/substitute.jl +++ b/src/substitute.jl @@ -16,7 +16,7 @@ julia> substitute(1+sqrt(y), Dict(y => 2), fold=false) function substitute(expr, dict; fold=true) haskey(dict, expr) && return dict[expr] - if istree(expr) + if iscall(expr) op = substitute(operation(expr), dict; fold=fold) if fold canfold = !(op isa Symbolic) @@ -53,7 +53,7 @@ Base.occursin(needle::Symbolic, haystack) = _occursin(needle, haystack) function _occursin(needle, haystack) isequal(needle, haystack) && return true - if istree(haystack) + if iscall(haystack) args = arguments(haystack) for arg in args occursin(needle, arg) && return true diff --git a/src/types.jl b/src/types.jl index 840f8a6e1..ec58b618e 100644 --- a/src/types.jl +++ b/src/types.jl @@ -102,7 +102,7 @@ symtype(x::Number) = typeof(x) @inline symtype(::Symbolic{T}) where T = T # We're returning a function pointer -@inline function operation(x::BasicSymbolic) +@inline function head(x::BasicSymbolic) @compactified x::BasicSymbolic begin Term => x.f Add => (+) @@ -113,6 +113,7 @@ symtype(x::Number) = typeof(x) _ => error_on_type() end end +@inline operation(x) = head(x) function arguments(x::BasicSymbolic) args = unsorted_arguments(x) @@ -183,7 +184,8 @@ function unsorted_arguments(x::BasicSymbolic) return args end -istree(s::BasicSymbolic) = !issym(s) +isexpr(s::BasicSymbolic) = !issym(s) +iscall(s::BasicSymbolic) = true @inline isa_SymType(T::Val{S}, x) where {S} = x isa BasicSymbolic ? Unityper.isa_type_fun(Val(SymbolicUtils.BasicSymbolic), T, x) : false issym(x::BasicSymbolic) = isa_SymType(Val(:Sym), x) isterm(x) = isa_SymType(Val(:Term), x) @@ -400,7 +402,7 @@ end @inline function numerators(x) isdiv(x) && return numerators(x.num) - istree(x) && operation(x) === (*) ? arguments(x) : Any[x] + iscall(x) && operation(x) === (*) ? arguments(x) : Any[x] end @inline denominators(x) = isdiv(x) ? numerators(x.den) : Any[1] @@ -516,7 +518,7 @@ end Binarizes `Term`s with n-ary operations """ function unflatten(t::Symbolic{T}) where{T} - if istree(t) + if iscall(t) f = operation(t) if f == (+) || f == (*) # TODO check out for other n-ary --> binary ops a = arguments(t) @@ -545,7 +547,7 @@ different type than `t`, because `f` also influences the result. resulting similar term to this type. """ similarterm(t::Symbolic, f, args; metadata=nothing) = - similarterm(t, f, args, _promote_symtype(f, args); metadata=metadata) + maketerm(typeof(t), f, args, type=_promote_symtype(f, args), metadata=metadata) similarterm(t::BasicSymbolic, f, args, symtype; metadata=nothing) = basic_similarterm(t, f, args, symtype; metadata=metadata) @@ -580,16 +582,17 @@ function hasmetadata(s::Symbolic, ctx) metadata(s) isa AbstractDict && haskey(metadata(s), ctx) end -function issafecanon(f, s) +issafecanon(f, s) = true +function issafecanon(f, s::Symbolic) if isnothing(metadata(s)) || issym(s) return true else _issafecanon(f, s) end end -_issafecanon(::typeof(*), s) = !istree(s) || !(operation(s) in (+,*,^)) -_issafecanon(::typeof(+), s) = !istree(s) || !(operation(s) in (+,*)) -_issafecanon(::typeof(^), s) = !istree(s) || !(operation(s) in (*, ^)) +_issafecanon(::typeof(*), s) = !iscall(s) || !(operation(s) in (+,*,^)) +_issafecanon(::typeof(+), s) = !iscall(s) || !(operation(s) in (+,*)) +_issafecanon(::typeof(^), s) = !iscall(s) || !(operation(s) in (*, ^)) issafecanon(f, ss...) = all(x->issafecanon(f, x), ss) @@ -641,7 +644,7 @@ end function to_symbolic(x) Base.depwarn("`to_symbolic(x)` is deprecated, define the interface for your " * - "symbolic structure using `istree(x)`, `operation(x)`, `arguments(x)` " * + "symbolic structure using `iscall(x)`, `operation(x)`, `arguments(x)` " * "and `similarterm(::YourType, f, args, symtype)`", :to_symbolic, force=true) x @@ -654,7 +657,7 @@ const show_simplified = Ref(false) isnegative(t::Real) = t < 0 function isnegative(t) - if istree(t) && operation(t) === (*) + if iscall(t) && operation(t) === (*) coeff = first(arguments(t)) return isnegative(coeff) end @@ -666,7 +669,7 @@ setargs(t, args) = Term{symtype(t)}(operation(t), args) cdrargs(args) = setargs(t, cdr(args)) print_arg(io, x::Union{Complex, Rational}; paren=true) = print(io, "(", x, ")") -isbinop(f) = istree(f) && !istree(operation(f)) && Base.isbinaryoperator(nameof(operation(f))) +isbinop(f) = iscall(f) && iscall(operation(f)) && Base.isbinaryoperator(nameof(operation(f))) function print_arg(io, x; paren=false) if paren && isbinop(x) print(io, "(", x, ")") @@ -685,7 +688,7 @@ function print_arg(io, f, x) end function remove_minus(t) - !istree(t) && return -t + iscall(t) && return -t @assert operation(t) == (*) args = arguments(t) @assert args[1] < 0 @@ -756,9 +759,9 @@ function show_ref(io, f, args) x = args[1] idx = args[2:end] - istree(x) && print(io, "(") + iscall(x) && print(io, "(") print(io, x) - istree(x) && print(io, ")") + iscall(x) && print(io, ")") print(io, "[") for i=1:length(idx) print_arg(io, idx[i]) @@ -768,7 +771,7 @@ function show_ref(io, f, args) end function show_call(io, f, args) - fname = istree(f) ? Symbol(repr(f)) : nameof(f) + fname = iscall(f) ? Symbol(repr(f)) : nameof(f) len_args = length(args) if Base.isunaryoperator(fname) && len_args == 1 print(io, "$fname") @@ -810,7 +813,7 @@ function show_term(io::IO, t) show_pow(io, args) elseif f === (getindex) show_ref(io, f, args) - elseif f === (identity) && !issym(args[1]) && !istree(args[1]) + elseif f === (identity) && !issym(args[1]) && iscall(args[1]) show(io, args[1]) else show_call(io, f, args) diff --git a/src/utils.jl b/src/utils.jl index acf9e92d6..8e875c6e6 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -35,7 +35,7 @@ pow(x::Symbolic,y::Symbolic) = Base.:^(x,y) # Simplification utilities function has_trig_exp(term) - !istree(term) && return false + !iscall(term) && return false fns = (sin, cos, tan, cot, sec, csc, exp, cosh, sinh) op = operation(term) @@ -47,7 +47,7 @@ function has_trig_exp(term) end function fold(t) - if istree(t) + if iscall(t) tt = map(fold, arguments(t)) if !any(x->x isa Symbolic, tt) # evaluate it @@ -81,7 +81,7 @@ function isnotflat(⋆) function (x) args = arguments(x) for t in args - if istree(t) && operation(t) === (⋆) + if iscall(t) && operation(t) === (⋆) return true end end @@ -141,7 +141,7 @@ function flatten_term(⋆, x) # flatten nested ⋆ flattened_args = [] for t in args - if istree(t) && operation(t) === (⋆) + if iscall(t) && operation(t) === (⋆) append!(flattened_args, arguments(t)) else push!(flattened_args, t) @@ -170,7 +170,7 @@ struct LL{V} i::Int end -islist(x) = istree(x) || !isempty(x) +islist(x) = iscall(x) || !isempty(x) Base.empty(l::LL) = empty(l.v) Base.isempty(l::LL) = l.i > length(l.v) @@ -184,9 +184,9 @@ Base.isempty(t::Term) = false @inline car(t::Term) = operation(t) @inline cdr(t::Term) = arguments(t) -@inline car(v) = istree(v) ? operation(v) : first(v) +@inline car(v) = iscall(v) ? operation(v) : first(v) @inline function cdr(v) - if istree(v) + if iscall(v) arguments(v) else islist(v) ? LL(v, 2) : error("asked cdr of empty") @@ -200,7 +200,7 @@ end if n === 0 return ll else - istree(ll) ? drop_n(arguments(ll), n-1) : drop_n(cdr(ll), n-1) + iscall(ll) ? drop_n(arguments(ll), n-1) : drop_n(cdr(ll), n-1) end end @inline drop_n(ll::Union{Tuple, AbstractArray}, n) = drop_n(LL(ll, 1), n) @@ -219,7 +219,7 @@ macro matchable(expr) fields = map(get_name, fields) quote $expr - SymbolicUtils.istree(::$name) = true + SymbolicUtils.iscall(::$name) = true SymbolicUtils.operation(::$name) = $name SymbolicUtils.arguments(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) Base.length(x::$name) = $(length(fields) + 1) @@ -229,7 +229,7 @@ end """ node_count(t) -Count the nodes in a symbolic expression tree satisfying `istree` and `arguments`. +Count the nodes in a symbolic expression tree satisfying `iscall` and `arguments`. """ -node_count(t) = istree(t) ? reduce(+, node_count(x) for x in arguments(t), init = 0) + 1 : 1 +node_count(t) = iscall(t) ? reduce(+, node_count(x) for x in arguments(t), init = 0) + 1 : 1 diff --git a/test/interface.jl b/test/interface.jl index d98d97328..af83fc89f 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -1,5 +1,5 @@ using SymbolicUtils, Test -import SymbolicUtils: istree, issym, operation, arguments, symtype +import SymbolicUtils: iscall, issym, operation, arguments, symtype issym(s::Symbol) = true Base.nameof(s::Symbol) = s From 85cb293ede3a48009f0bada59f42188588a60edd Mon Sep 17 00:00:00 2001 From: a Date: Sun, 10 Mar 2024 19:18:40 +0100 Subject: [PATCH 03/18] adjust --- src/types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.jl b/src/types.jl index ec58b618e..d0540b00f 100644 --- a/src/types.jl +++ b/src/types.jl @@ -185,7 +185,7 @@ function unsorted_arguments(x::BasicSymbolic) end isexpr(s::BasicSymbolic) = !issym(s) -iscall(s::BasicSymbolic) = true +iscall(s::BasicSymbolic) = isexpr(s) @inline isa_SymType(T::Val{S}, x) where {S} = x isa BasicSymbolic ? Unityper.isa_type_fun(Val(SymbolicUtils.BasicSymbolic), T, x) : false issym(x::BasicSymbolic) = isa_SymType(Val(:Sym), x) isterm(x) = isa_SymType(Val(:Term), x) From 7c0c97ab0b89b1dfdaefebe85fcf726573634252 Mon Sep 17 00:00:00 2001 From: a Date: Sun, 10 Mar 2024 19:29:43 +0100 Subject: [PATCH 04/18] adjust --- src/types.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.jl b/src/types.jl index d0540b00f..82e540f5a 100644 --- a/src/types.jl +++ b/src/types.jl @@ -669,7 +669,7 @@ setargs(t, args) = Term{symtype(t)}(operation(t), args) cdrargs(args) = setargs(t, cdr(args)) print_arg(io, x::Union{Complex, Rational}; paren=true) = print(io, "(", x, ")") -isbinop(f) = iscall(f) && iscall(operation(f)) && Base.isbinaryoperator(nameof(operation(f))) +isbinop(f) = iscall(f) && !iscall(operation(f)) && Base.isbinaryoperator(nameof(operation(f))) function print_arg(io, x; paren=false) if paren && isbinop(x) print(io, "(", x, ")") @@ -688,7 +688,7 @@ function print_arg(io, f, x) end function remove_minus(t) - iscall(t) && return -t + !iscall(t) && return -t @assert operation(t) == (*) args = arguments(t) @assert args[1] < 0 From 4fa745b8d07f962be11649c37fa5101732d852fc Mon Sep 17 00:00:00 2001 From: a Date: Mon, 11 Mar 2024 07:51:12 +0100 Subject: [PATCH 05/18] adjust similarterm --- src/types.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types.jl b/src/types.jl index 82e540f5a..be1a4b4a9 100644 --- a/src/types.jl +++ b/src/types.jl @@ -546,8 +546,8 @@ different type than `t`, because `f` also influences the result. - The `symtype` of the resulting term. Best effort will be made to set the symtype of the resulting similar term to this type. """ -similarterm(t::Symbolic, f, args; metadata=nothing) = - maketerm(typeof(t), f, args, type=_promote_symtype(f, args), metadata=metadata) +similarterm(t::Symbolic, f, args, symtype; metadata=nothing) = + maketerm(typeof(t), f, args, _promote_symtype(f, args), metadata) similarterm(t::BasicSymbolic, f, args, symtype; metadata=nothing) = basic_similarterm(t, f, args, symtype; metadata=metadata) From 98091a602418d41821fdd7b228b81cbdc53a30c9 Mon Sep 17 00:00:00 2001 From: Niklas Heim Date: Thu, 14 Mar 2024 15:06:14 +0100 Subject: [PATCH 06/18] import TI.metadata --- src/SymbolicUtils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index cf34a0563..1140cbb0e 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -17,7 +17,7 @@ using SymbolicIndexingInterface import Base: +, -, *, /, //, \, ^, ImmutableDict using ConstructionBase using TermInterface -import TermInterface: iscall, isexpr, issym, symtype, head, children, operation, arguments +import TermInterface: iscall, isexpr, issym, symtype, head, children, operation, arguments, metadata function similarterm end include("types.jl") From 6e7e43a604ebd6c19771865fd621aaf30519c595 Mon Sep 17 00:00:00 2001 From: Niklas Heim Date: Thu, 14 Mar 2024 15:57:56 +0100 Subject: [PATCH 07/18] arguments -> children --- src/types.jl | 1 + src/utils.jl | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types.jl b/src/types.jl index be1a4b4a9..09f8e75fe 100644 --- a/src/types.jl +++ b/src/types.jl @@ -136,6 +136,7 @@ function arguments(x::BasicSymbolic) end return args end +children(x::BasicSymbolic) = arguments(x) function unsorted_arguments(x::BasicSymbolic) @compactified x::BasicSymbolic begin Term => return x.arguments diff --git a/src/utils.jl b/src/utils.jl index 8e875c6e6..4058a8de2 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -221,7 +221,7 @@ macro matchable(expr) $expr SymbolicUtils.iscall(::$name) = true SymbolicUtils.operation(::$name) = $name - SymbolicUtils.arguments(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) + SymbolicUtils.children(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) Base.length(x::$name) = $(length(fields) + 1) SymbolicUtils.similarterm(x::$name, f, args, type; kw...) = f(args...) end |> esc From d39da530f887a2e01ef4fe8f9d2aa0bab36ed757 Mon Sep 17 00:00:00 2001 From: Niklas Heim Date: Thu, 14 Mar 2024 16:27:41 +0100 Subject: [PATCH 08/18] define maketerm --- src/types.jl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/types.jl b/src/types.jl index 09f8e75fe..c78bc0308 100644 --- a/src/types.jl +++ b/src/types.jl @@ -547,10 +547,12 @@ different type than `t`, because `f` also influences the result. - The `symtype` of the resulting term. Best effort will be made to set the symtype of the resulting similar term to this type. """ -similarterm(t::Symbolic, f, args, symtype; metadata=nothing) = - maketerm(typeof(t), f, args, _promote_symtype(f, args), metadata) -similarterm(t::BasicSymbolic, f, args, - symtype; metadata=nothing) = basic_similarterm(t, f, args, symtype; metadata=metadata) +similarterm(t::Symbolic, f, args; metadata=nothing) = + maketerm(typeof(t), f, args, _promote_symtype(f, args); metadata) +similarterm(t::BasicSymbolic, f, args, symtype; metadata=nothing) = + maketerm(typeof(t), f, args, symtype; metadata=metadata) +maketerm(T::Type{<:Symbolic}, f, args, symtype; metadata=nothing) = + basic_similarterm(T, f, args, symtype; metadata=metadata) function basic_similarterm(t, f, args, stype; metadata=nothing) if f isa Symbol From b54c5f8fde587b72910fdc81a4ce57688a7a9a8c Mon Sep 17 00:00:00 2001 From: Niklas Heim Date: Thu, 14 Mar 2024 16:39:37 +0100 Subject: [PATCH 09/18] get missing ! back --- src/types.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types.jl b/src/types.jl index c78bc0308..9a9036605 100644 --- a/src/types.jl +++ b/src/types.jl @@ -816,7 +816,7 @@ function show_term(io::IO, t) show_pow(io, args) elseif f === (getindex) show_ref(io, f, args) - elseif f === (identity) && !issym(args[1]) && iscall(args[1]) + elseif f === (identity) && !issym(args[1]) && !iscall(args[1]) show(io, args[1]) else show_call(io, f, args) From f956330a671653bb0f12439fd15582052bd123bd Mon Sep 17 00:00:00 2001 From: Niklas Heim Date: Thu, 14 Mar 2024 17:30:45 +0100 Subject: [PATCH 10/18] add arguments overload --- src/utils.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.jl b/src/utils.jl index 4058a8de2..a12800843 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -222,6 +222,7 @@ macro matchable(expr) SymbolicUtils.iscall(::$name) = true SymbolicUtils.operation(::$name) = $name SymbolicUtils.children(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) + SymbolicUtils.arguments(x::$name) = SymbolicUtils.children(x) Base.length(x::$name) = $(length(fields) + 1) SymbolicUtils.similarterm(x::$name, f, args, type; kw...) = f(args...) end |> esc From dfed907e5a415a1914019d739a57cb4ed9753227 Mon Sep 17 00:00:00 2001 From: Niklas Heim Date: Thu, 14 Mar 2024 17:51:07 +0100 Subject: [PATCH 11/18] add head overload --- src/utils.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/utils.jl b/src/utils.jl index a12800843..6c0474890 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -220,6 +220,7 @@ macro matchable(expr) quote $expr SymbolicUtils.iscall(::$name) = true + SymbolicUtils.head(::$name) = $name SymbolicUtils.operation(::$name) = $name SymbolicUtils.children(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) SymbolicUtils.arguments(x::$name) = SymbolicUtils.children(x) From dec676632d85d33a4c32ba525bea33d39aa1844d Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Fri, 15 Mar 2024 15:36:23 -0400 Subject: [PATCH 12/18] stash --- src/SymbolicUtils.jl | 4 +-- src/types.jl | 60 ++++++++++++++++++++++++++++---------------- 2 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index 345481c2e..6386b9a34 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -15,12 +15,12 @@ import Setfield: PropertyLens using SymbolicIndexingInterface import Base: +, -, *, /, //, \, ^, ImmutableDict using ConstructionBase -include("interface.jl") +import TermInterface: iscall, issym, operation, arguments, head, children, similarterm, maketerm # Sym, Term, # Add, Mul and Pow include("types.jl") -export iscall, operation, arguments, similarterm +export iscall, operation, arguments, similarterm, maketerm # Methods on symbolic objects using SpecialFunctions, NaNMath diff --git a/src/types.jl b/src/types.jl index 452eeb18c..c7c53a413 100644 --- a/src/types.jl +++ b/src/types.jl @@ -157,7 +157,7 @@ function unsorted_arguments(x::BasicSymbolic) if isadd(x) for (k, v) in x.dict push!(args, applicable(*,k,v) ? k*v : - similarterm(k, *, [k, v])) + maketerm(k, *, [k, v])) end else # MUL for (k, v) in x.dict @@ -192,6 +192,9 @@ isadd(x) = isa_SymType(Val(:Add), x) ispow(x) = isa_SymType(Val(:Pow), x) isdiv(x) = isa_SymType(Val(:Div), x) +TermInterface.head(::BasicSymbolic) = basicsymbolic +TermInterface.children(t::BasicSymbolic) = cons(operation(t), arguments(t)) + ### ### Base interface ### @@ -528,28 +531,11 @@ end unflatten(t) = t -""" - similarterm(t, f, args, symtype; metadata=nothing) - -Create a term that is similar in type to `t`. Extending this function allows packages -using their own expression types with SymbolicUtils to define how new terms should -be created. Note that `similarterm` may return an object that has a -different type than `t`, because `f` also influences the result. - -## Arguments - -- `t` the reference term to use to create similar terms -- `f` is the operation of the term -- `args` is the arguments -- The `symtype` of the resulting term. Best effort will be made to set the symtype of the - resulting similar term to this type. -""" -similarterm(t::Symbolic, f, args; metadata=nothing) = - similarterm(t, f, args, _promote_symtype(f, args); metadata=metadata) -similarterm(t::BasicSymbolic, f, args, - symtype; metadata=nothing) = basic_similarterm(t, f, args, symtype; metadata=metadata) +function TermInterface.maketerm(::Type{<:BasicSymbolic}, head, args, type, metadata) + basicsymbolic(first(args), args[2:end], type, metadata) +end -function basic_similarterm(t, f, args, stype; metadata=nothing) +function basicsymbolic(f, args, stype, metadata) if f isa Symbol error("$f must not be a Symbol") end @@ -647,6 +633,36 @@ function to_symbolic(x) x end +""" + similarterm(x, op, args, symtype=nothing; metadata=nothing) + +""" +function similarterm(x, op, args, symtype=nothing; metadata=nothing) + Base.depwarn("""`similarterm` is deprecated, use `maketerm` instead. + See https://github.com/JuliaSymbolics/TermInterface.jl for details. + The present call can be replaced by + `maketerm(typeof(x), $(head(x)), [op, args...], symtype, metadata)`""", :similarterm) + + TermInterface.maketerm(typeof(x), callhead(x), [op, args...], symtype, metadata) +end + +# Old fallback +function similarterm(T::Type, op, args, symtype=nothing; metadata=nothing) + Base.depwarn("`similarterm` is deprecated, use `maketerm` instead." * + "See https://github.com/JuliaSymbolics/TermInterface.jl for details.", :similarterm) + op(args...) +end + +export similarterm + + +""" + callhead(x) +Used in this deprecation cycle of `similarterm` to find the `head` argument to +`maketerm`. Do not implement this, or use `similarterm` if you're using this package. +""" +callhead(x) = typeof(x) + ### ### Pretty printing ### From 49b30a0b3a88e35d4e25c88b323b629645d53ac6 Mon Sep 17 00:00:00 2001 From: a Date: Mon, 25 Mar 2024 11:28:57 +0100 Subject: [PATCH 13/18] disable broken tests --- test/runtests.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 004b26b7d..3098331ab 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,6 +33,7 @@ else include("code.jl") include("cse.jl") include("interface.jl") - include("fuzz.jl") + # Disabled until https://github.com/JuliaMath/SpecialFunctions.jl/issues/446 is fixed + # include("fuzz.jl") include("adjoints.jl") end From 9c6965e79f9f7336bba63e6caec6fa45b8074c0c Mon Sep 17 00:00:00 2001 From: a Date: Mon, 25 Mar 2024 21:52:21 +0100 Subject: [PATCH 14/18] adjust --- Project.toml | 2 +- src/inspect.jl | 2 +- src/polyform.jl | 5 +++-- src/types.jl | 6 +++--- src/utils.jl | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Project.toml b/Project.toml index eb02984e5..c75c5a374 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "SymbolicUtils" uuid = "d1185830-fcd6-423d-90d6-eec64667417b" authors = ["Shashi Gowda"] -version = "1.5.1" +version = "1.6.0" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" diff --git a/src/inspect.jl b/src/inspect.jl index ca1970dd5..e58fb9179 100644 --- a/src/inspect.jl +++ b/src/inspect.jl @@ -27,7 +27,7 @@ function AbstractTrees.nodevalue(x::BasicSymbolic) end function AbstractTrees.children(x::Symbolic) - isexpr(x) ? children(x) : () + isexpr(x) ? arguments(x) : () end """ diff --git a/src/polyform.jl b/src/polyform.jl index 8d9474cd5..3bf25ca6d 100644 --- a/src/polyform.jl +++ b/src/polyform.jl @@ -184,8 +184,8 @@ function similarterm(::PolyForm, f::Union{typeof(*), typeof(+), typeof(^)}, f(args...) end -head(x::PolyForm) = MP.nterms(x.p) == 1 ? (*) : (+) -operation(x::PolyForm) = head(x) +head(::PolyForm) = PolyForm +operation(x::PolyForm) = MP.nterms(x.p) == 1 ? (*) : (+) function arguments(x::PolyForm{T}) where {T} @@ -231,6 +231,7 @@ function arguments(x::PolyForm{T}) where {T} PolyForm{T}(t, x.pvar2sym, x.sym2term, nothing)) for t in ts] end end +children(x::PolyForm) = [operation(x); arguments(x)] Base.show(io::IO, x::PolyForm) = show_term(io, x) diff --git a/src/types.jl b/src/types.jl index d40cf4d11..1574cfa01 100644 --- a/src/types.jl +++ b/src/types.jl @@ -102,7 +102,7 @@ symtype(x::Number) = typeof(x) @inline symtype(::Symbolic{T}) where T = T # We're returning a function pointer -@inline function head(x::BasicSymbolic) +@inline function operation(x::BasicSymbolic) @compactified x::BasicSymbolic begin Term => x.f Add => (+) @@ -113,7 +113,7 @@ symtype(x::Number) = typeof(x) _ => error_on_type() end end -@inline operation(x) = head(x) +@inline head(x::BasicSymbolic) = BasicSymbolic function arguments(x::BasicSymbolic) args = unsorted_arguments(x) @@ -136,7 +136,7 @@ function arguments(x::BasicSymbolic) end return args end -children(x::BasicSymbolic) = arguments(x) +children(x::BasicSymbolic) = [operation(x); arguments(x)] function unsorted_arguments(x::BasicSymbolic) @compactified x::BasicSymbolic begin Term => return x.arguments diff --git a/src/utils.jl b/src/utils.jl index 6c0474890..619773064 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -222,8 +222,8 @@ macro matchable(expr) SymbolicUtils.iscall(::$name) = true SymbolicUtils.head(::$name) = $name SymbolicUtils.operation(::$name) = $name - SymbolicUtils.children(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) - SymbolicUtils.arguments(x::$name) = SymbolicUtils.children(x) + SymbolicUtils.arguments(x::$name) = getfield.((x,), ($(QuoteNode.(fields)...),)) + SymbolicUtils.children(x::$name) = [SymbolicUtils.operation(x); SymbolicUtils.children(x)] Base.length(x::$name) = $(length(fields) + 1) SymbolicUtils.similarterm(x::$name, f, args, type; kw...) = f(args...) end |> esc From 9d86f9ec90fc657c6a1eb129e1765b9a053a5134 Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Thu, 11 Apr 2024 21:48:28 -0400 Subject: [PATCH 15/18] keep istree but deprecate --- src/SymbolicUtils.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index d6a7bdf3b..71cb56647 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -23,6 +23,7 @@ import TermInterface: iscall, isexpr, issym, symtype, head, children, # Add, Mul and Pow include("types.jl") export similarterm +Base.@deprecate_binding istree iscall # Methods on symbolic objects using SpecialFunctions, NaNMath From ef0cd4d6811c897cf79d5fd02c9affcb8fd493bc Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Fri, 12 Apr 2024 08:32:31 -0400 Subject: [PATCH 16/18] reexport old interface --- src/SymbolicUtils.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index 71cb56647..7cff2f1c4 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -19,11 +19,11 @@ using TermInterface import TermInterface: iscall, isexpr, issym, symtype, head, children, operation, arguments, metadata, maketerm +Base.@deprecate_binding istree iscall +export istree, operation, arguments, similarterm # Sym, Term, # Add, Mul and Pow include("types.jl") -export similarterm -Base.@deprecate_binding istree iscall # Methods on symbolic objects using SpecialFunctions, NaNMath From c679e9bd149997361795f9e4fa28914e4392215b Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Fri, 12 Apr 2024 18:04:30 -0400 Subject: [PATCH 17/18] reexport unsorted_arguments --- src/SymbolicUtils.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SymbolicUtils.jl b/src/SymbolicUtils.jl index 7cff2f1c4..286ba3bc1 100644 --- a/src/SymbolicUtils.jl +++ b/src/SymbolicUtils.jl @@ -20,7 +20,7 @@ import TermInterface: iscall, isexpr, issym, symtype, head, children, operation, arguments, metadata, maketerm Base.@deprecate_binding istree iscall -export istree, operation, arguments, similarterm +export istree, operation, arguments, unsorted_arguments, similarterm # Sym, Term, # Add, Mul and Pow include("types.jl") From ed97a96a8864c584c12c040acdfdf09380d2e8eb Mon Sep 17 00:00:00 2001 From: Shashi Gowda Date: Sat, 13 Apr 2024 11:31:50 -0400 Subject: [PATCH 18/18] add a catch-all unsorted_arguments --- src/types.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/types.jl b/src/types.jl index 489c633ab..f9ea33121 100644 --- a/src/types.jl +++ b/src/types.jl @@ -137,6 +137,8 @@ function arguments(x::BasicSymbolic) end return args end + +unsorted_arguments(x) = arguments(x) children(x::BasicSymbolic) = [operation(x); arguments(x)] function unsorted_arguments(x::BasicSymbolic) @compactified x::BasicSymbolic begin