JuliaSymbolics / SymbolicUtils.jl

Symbolic expressions, rewriting and simplification
https://docs.sciml.ai/SymbolicUtils/stable/
Other
537 stars 107 forks source link

Converting types to predicates for rule matching constraints #630

Open jpfairbanks opened 1 month ago

jpfairbanks commented 1 month ago

In trying to solve #629 with rewriting I came up with some rules:

abstract type Form <: Number end
struct Form0 <: Form end
struct Form1 <: Form end
struct Form2 <: Form end

d(::Form)::Form
d₀(::Form0)::Form1
# these don't work because Form0/Form1 are types and not predicates that check for types
@rule d(~x::Form0) => d₀(~x)
@rule d(~x::Form1) => d₁(~x)

# these functions fix it because we can match on predicates
form0p(x) = symtype(x) == Form0
form1p(x) = symtype(x) == Form1

@rule d(~x::form0p) => d₀(~x)
@rule d(~x::form1p) => d₁(~x)

Is there a way that we could hook into @rule so that if you tried to match on a type it automatically lifts that type to a predicate?

jpfairbanks commented 1 month ago

A workaround that I found was to use the rule level predicates and an auxiliary function hastype. My macro for specifying these uses the following to insert a call to hastype which checks the symbolic type.

hastype(x, T) = symtype(x) == T
@rule $op(~x) => (hastype(~x,$src_type) ? $resolveto(~x) : nothing)

So the rules end up looking like

d(~x) => if hastype(~x, Form0)
        d₀(~x)
    else
        nothing
    end

but I'm not writing that verbosity because of an intermediate macro.

Just putting d(~x::Form0) in the LHS would be much more concise.