Symbolics.jl
Symbolics.jl copied to clipboard
Rules work with @syms but not with @variables
Hi!
I noticed the following behavior similar to what was mentioned in this issue. When I apply a rule to a function defined via "@variables" it does not work, while with "@syms" everything is fine. Here is a minimal example:
using Symbolics
@variables a(..)
@syms b(i1, i2)::Real
println("types are the same: ", (a(2, 1) |> typeof) == (b(2, 1) |> typeof)) # prints true
ra = @rule a(~i1, ~i2) => a(~i2, ~i1) where {~i1>~i2}
rb = @rule b(~i1, ~i2) => b(~i2, ~i1) where {~i1>~i2}
println("result of the rule application on a: ", ra(a(2, 1))) # prints nothing
println("result of the rule application on b: ", rb(b(2, 1))) # prints b(1,2)
There is no documentation on pattern matching, so I don't know if this is intended or not.
It needs to unwrap. It would be nice if we made them auto-unwrap and wrap.
What should be unwrapped exactly? I tried ra(Symbolics.unwrap(a(2, 1))). This does not change the result. Also, note that there is the test showing that a(2,1) and b(2,1) are of the same type.
Sounds like there's something deeper going on here then.
Hello, i explored this issue but I have some question I cannot resolve myself. Here are my findings and questions:
This rule application doesnt work because a and b are of different types. As pointed out by @ebelnikola a(2,1) and b(2,1) are of the same type, that is:
julia> typeof(a(2,1))
SymbolicUtils.BasicSymbolic{Real}
, but:
julia> typeof(a)
Symbolics.CallWithMetadata{SymbolicUtils.FnType{Tuple, Real}, Base.ImmutableDict{DataType, Any}}
julia> typeof(b)
SymbolicUtils.BasicSymbolic{SymbolicUtils.FnType{Tuple{Number, Number}, Real, Nothing}}
The code responsible for this is the function matcher from SymbolicUtils:
https://github.com/JuliaSymbolics/SymbolicUtils.jl/blob/2f8d4faed52a4e40ce419261a377c86fd1b0c889/src/matchers.jl#L8C1-L13C4
it is called when the rule is definied, and returns another function that gets assigns to the rule object, and gets called every time that the rule gets applied. The variable val represent the left hand side of the rule (the thing we need to check against). In this particular example is of type Symbolics.CallWithMetadata, and the rule application fails bc it doesnt match with car(data) of type SymbolicUtils.BasicSymbolic{SymbolicUtils.FnType{Tuple, Real}} (data is what is passed when calling the rule, so a(2,1) in this case).
One potential fix could be to check whether the left hand side of a rule is a Symbolics.CallWithMetadata, and in that case use instead of val its argumet val.f, something like this:
function matcher(val::Any)
iscall(val) && return term_matcher(val)
if # val isa Symbolics.CallWithMetadata
val = val.f
end
function literal_matcher(next, data, bindings)
islist(data) && isequal(car(data), val) ? next(bindings, 1) : nothing
end
end
. But as you can see i put a comment instead of the if condition because i get the error:
ERROR: UndefVarError: `Symbolics` not defined in `SymbolicUtils`
Suggestion: check for spelling errors or missing imports.
Hint: a global variable of this name also exists in Symbolics.
Stacktrace:
[1] matcher(val::Symbolics.CallWithMetadata{SymbolicUtils.FnType{Tuple, Real}, Base.ImmutableDict{DataType, Any}})
@ SymbolicUtils ~/.julia/dev/SymbolicUtils/src/matchers.jl:13
[2] term_matcher(term::SymbolicUtils.BasicSymbolic{Any})
@ SymbolicUtils ~/.julia/dev/SymbolicUtils/src/matchers.jl:117
[3] matcher(val::SymbolicUtils.BasicSymbolic{Any})
@ SymbolicUtils ~/.julia/dev/SymbolicUtils/src/matchers.jl:11
[4] top-level scope
@ ~/.julia/dev/SymbolicUtils/src/rule.jl:321
Questions:
- What is the correct way to reference
Symbolics.CallWithMetadatatype from SymbolicUtils? - is this a good fix or just a patch and something deeper needs to be changed?
PS: with this code:
function matcher(val::Any)
iscall(val) && return term_matcher(val)
try
val = val.f
catch
end
function literal_matcher(next, data, bindings)
islist(data) && isequal(car(data), val) ? next(bindings, 1) : nothing
end
end
it works:
julia> ra(a(2, 1))
a(1, 2)