-
Notifications
You must be signed in to change notification settings - Fork 123
[WIP] first prototype of default value rules implemented #749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
[WIP] first prototype of default value rules implemented #749
Conversation
Benchmark Results
Benchmark PlotsA plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR. |
Nice! I'm a little concerned about ordering here. How does this handle commutative operations?
This could reasonably return any one of
This could return 1 or 2. How are these cases handled? |
@AayushSabharwal the symbols are reorderd in a kind of alphabetical order before being passed to the rule, we can also check it from julia repl:
being the rule macro not commutiative, is returned the vaule that matches the rule in the ordered expression, so:
or, another example:
I didn't think about this, but maybe for the symbolic integrator I need acrules... I dont know |
The printout in the REPL can be misleading. It uses
ACRules are likely necessary for some integration rules, but it doesn't help with the ambiguity. That said, I'm not sure if the ambiguity actually matters for integration. It seems all our existing matchers use |
Yes you are right, in fact after writing the message I found some cases I could not explain:
and in fact:
|
ok now an operation whith default value can match also with trees (others expressions) like:
previously was only with single symbols like
atterntion!A thing that I dont like is: in matchers.jl in the
The operation is saved in the default value in rules.jl in the I'd like to find a way without using strings |
src/matchers.jl
Outdated
# if data is not a list, return nothing | ||
!islist(data) && return nothing | ||
# if data (is not a tree and is just a symbol) or (is a tree not starting with the default operation) | ||
if !iscall(car(data)) || (istree(car(data)) && string(defslot.operation) != string(operation(car(data)))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this use string comparison?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, #749 (comment) didn't load for me until I refreshed the page. That behavior is... weird. However, you should be able to match nameof(operation(...))
with defslot.operation
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also FYI the value in DefSlot
is a Symbol
because it comes from parsing an Expr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks I didn't know of nameof, I will use it now
src/rule.jl
Outdated
return 1 | ||
end | ||
# else no default value for this call | ||
return nothing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why return nothing
instead of erroring?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah I put it there at the beginning and forgot, is better to trow an error. I will add it now
what are default value rules
Reading Mathematica pattern matching documantation (here) I saw they have a pattern matching with slots that can take a default value. In Mathematica a generic pattern named "a" is written a_ (in julia symbolics ~a ). But then if I write a_. it matches a pattern with built in default value, so basically (a_. + b_*x_) can match 2x where "a" takes the default value of 0 (and b=2 of course). or another example (a_ + b_*x_)^m_. can match (1 + 2x) where m takes the default value of 1 (and a=1 b=2 of course), whereas the pattern (a_ + b_*x_)^m_ would not match (1 + 2x) because the exponent m needs to be there and cannot take a default value.
why i do them
This feature is needed for my GSoC project of implementing a rule based integrator in julia symbolics.
what i did
Now I implemented this default value for sum and multiplication.
I choose the syntax of this new slot to be
~!x
(but i could choose any function that is one charachter and has one argument, for example also √), so for example:whereas
Note that you don't need to put the exclamation point also after the
=>
. I don't kow if this is ok or needs to be changed.how i did it
i created a new type in
rules.jl
calledDefSlot
, alongside the already presentSlot
(used for~x
) andSegment
(used for~~x
). It's the same asSlot
but it has a field for the default value.At the creation of the rule with the
@rule
macro these things happen:The
makepattern
function creates a tree of slots, segments and defslots starting from the expression of the left hand side of the rule.a matcher is created, starting by calling the function
https://github.com/Bumblebee00/SymbolicUtils.jl/blob/a95aea2310ea12f1ee2d9b2850d062ec82a437c4/src/matchers.jl#L8
that calls a bunch of other functions returning a tree of stot matchers (for slots), segment matchers (for segments), literal matchers (for numbers and others), term matchers (for combination of the previous), and i added
term_matcher_defslot
that matches either a term (if the rule has all the pieces) or a single symbol (if the default value needs to be used)https://github.com/Bumblebee00/SymbolicUtils.jl/blob/66026d6f2e7b50250d05625b111836d9d9dd1842/src/matchers.jl#L145-L176