Skip to content

Commit

Permalink
Add mmatera's information on Rules and application
Browse files Browse the repository at this point in the history
  • Loading branch information
rocky committed Sep 16, 2024
1 parent 5adbdc1 commit d3e99ab
Showing 1 changed file with 44 additions and 7 deletions.
51 changes: 44 additions & 7 deletions mathics/core/rules.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,51 @@
# -*- coding: utf-8 -*-
"""Rules are a core part of the way WMA and Mathics3 executes a
"""Rules are a core part of the way Mathematica and Mathics3 execute a
program.
Expressions which are transformed by rewrite rules (AKA transformation
rules) are handed by the Rule class.
rules) are handed by the `Rule` class.
There are also rules for how to match, assign parameter arguments, and
apply Mathics3 functions that are implemented in Python code. The
FunctionApplyRule class handles this.
There are also rules for how to match, assign function parameter
arguments, and then apply a Python "evaluation" function to a Mathics3 Expression.
These kinds of rules are handled by objects in the `FunctionApplyRule` class.
This module contains the classes for these two types of rules.
In a `FunctionApplyRule` rule, the match status of a rule depends on the evaluation return.
For example, suppose that we try to apply rule `F[x_]->x^2` to the expression `F[2]`. The pattern part of the rule,`F[x_]` matches
the expression, `Blank[x]` (or `x_`) is replaced by `2`, giving the substitution expression `2^2`. Evaluation then stops
looking for other rules to be applied over `F[2]`.
On the other hand, suppose that we define a `FunctionApplyRule` that associates `F[x_]` with the function:
```
...
class MyFunction(Builtin):
...
def eval_f(self, x, evaluation) -> Optional[Expression]:
"F[x_]" # pattern part of FunctionApplyRule
if x>3:
return Expression(SymbolPower, x, Integer2)
return None
```
Then, if we apply the rule to `F[2]`, the function is evaluated returning `None`. Then, in the evaluation loop, we get the same
effect as if the pattern didn't match with the expression. The loop continues then with the next rule associated with `F`.
Why do things this way?
Sometimes, the cost of deciding if the rule match is similar to the cost of evaluating the function. Suppose for example a rule
F[x_/;(G[x]>0)]:=G[x]
with G[x] a computationally expensive function. To decide if G[x] is larger than 0, we need to evaluate it,
and once we have evaluated it, just need to return its value.
Also, this allows us to handle several rules in the same function, without relying on our very slow pattern-matching routines.
In particular, this is used for for some critical low-level tasks like building lists in iterators, processing arithmetic expressions,
plotting functions, or evaluating derivatives and integrals.
"""


Expand Down Expand Up @@ -224,7 +260,8 @@ def __repr__(self) -> str:


class FunctionApplyRule(BaseRule):
"""A FunctionApplyRule is a rule that has a replacement term that
"""
A FunctionApplyRule is a rule that has a replacement term that
is associated a Python function rather than a Mathics Expression
as happens in a transformation Rule.
Expand Down Expand Up @@ -260,7 +297,7 @@ class FunctionApplyRule(BaseRule):
sets the attribute ``NumericFunction`` in the definition of the symbol ``F`` and
returns Null (``SymbolNull`)`.
This will cause `Expression.evalate() to perform an additional
This will cause `Expression.evaluate() to perform an additional
``rewrite_apply_eval()`` step.
"""
Expand Down

0 comments on commit d3e99ab

Please sign in to comment.