Rules and Pattern Matching
A common mathematical formula is
log(x)+log(y)==log(x*y)
for any x and y. The presence of the word "any" indicates that x and y
can stand for arbitrary mathematical expressions in the above formula. You
can use such mathematical formulas in Axiom to specify "rewrite rules".
Rewrite rules are objects in Axiom that can be assigned to variables for
later use, often for the purpose of simplification. Rewrite rules look like
ordinary function definitions except that they are preceded by the reserved
word rule. For example, a rewrite rule for the above formula is:
rule log(x) + log(y) == log(x * y)
Like function definitions, no action is taken when a rewrite rule is issued.
Think of rewrite rules as functions that take one argument. When a rewrite
rule A=B is applied to an argument f, its meaning is "rewrite every
subexpressions of f that matches A by B". The left-and side of a rewrite rule
is called a pattern;
its right-hand side is
called its substitution.
Create a rewrite rule named logrule. The generated symbol begins with a
"%" and is a place holder for any other terms that might occur in the sum.
Create an expression with logarithms.
Apply logrule to f.
The meaning of our example rewrite rule is "for all expressions x and y,
rewrite log(x) and log(y) by log(x*y)". Patterns generally have both operation
names
(here, log and +)
and variables (here, x and y). By default, every operation name stands for
itself. The log matches only "log" and not
any other operation such as sin. On the other
hand, variables do not stand for themselves. Rather, a variable denotes a
pattern variable
that is free to match any expression whatsoever.
When a rewrite rule is applied, a process called
pattern matching
goes to work by systematically
scanning the subexpressions of the argument. When a subexpression is found
that "matches" the pattern, the subexpression is replaced by the right hand
side of the rule. The details of what happens will be covered later.
The customary Axiom notation for patterns is actually a shorthand for a
longer, more general notation. Pattern variables can be made explicit
by using a percent ("%") as the first character of the variable name. To
say that a name stands for itself, you can prefix that name with a quote
operator ("'"). Although the current Axiom parser does not let you quote
an operation name, this more general notation gives you an alternative way
of giving the same rewrite rule:
rule log(%x) + log(%y) == log(x*y)
This longer notation gives you patterns that the standard notation won't
handle. For example, the rule
rule %f(c * 'x) == c*%f(x)
means "for all f and c, replace f(y) by c*f(x) when y is the product
of c and the explicit variable x".
Thus the pattern can have several adornments on the names that appear there.
Normally, all of these adornments are dropped in the substitution on the
right hand side. To summarize:
To enter a single rule in Axiom, use the following syntax:
rule lefthandside == righthandside
The lefthandside is a pattern to be matched and the righthandside is its
substitution. The rule is an object of type
RewriteRule that can be assigned to a
variable and applied to expressions to transform them.
Rewrite rules can be collected into rulesets so that a set of rules can be
applied at once. Here is another simplification rule for logarithms.
rule y*log(x) == log(x**y)
for any x and y. If instead of giving a single rule following the reserved
word rule you give a "pile" of rules, you create what is called a
ruleset. Like rules, rulesets are objects in Axiom and can be assigned to
variables. You will find it useful to group commonly used rules into
input files, and read them in as needed. Create a ruleset named logrules.
Again, create an expression f containing logarithms.
Apply the ruleset logrules to f.
We have allowed pattern variables to match arbitrary expressions in the
above examples. Often you want a variable to match onlyh expressions
satisfying some predicate. For example, you may want to apply the
transformation
y*log(x) == log(x^y)
only when y is an integer. The way to restrict a pattern variable y by a
predicate f(y) is by using a vertical bar "|", which means "such that",
in much the same way it is used in function definitions. You do this only
once but at the earliest (meaning deepest and leftmost) part of the pattern.
This restricts the logarithmic rule to create integer exponents only.
Compare this with the result of applying the previous set of rules.
You should be aware that you might need to apply a function like
integer within your predicate expression
to actually apply the test function. Here we use
integer because n has type
Expression Integer but
even? is an operation defined on the integers.
Here is the application of the rule.
This is an example of some of the usual identities involving products of sines and cosines.
Another qualification you will often want to use is to allow a pattern to
match an identity element. Using the pattern x+y, for example, neither x
nor y matches the expression 0. Similarly, if a pattern contains a product
x*y or an exponentiation x^y, then neither x nor y matches 1. If identical
elements were matched, pattern matching would generally loop. Here is an
expansion rule for exponentials.
This rule would cause infinite rewriting on this if either a or b were
allowed to match 0.
There are occasions when you do want a pattern variable in a sum or product
to match 0 or 1. If so, prefix its name with a "?" whenever it appears in
a left-hand side of a rule. For example, consider the following rule for the
exponential integral
integral((y+exp x)/x,x) == integral(y/x,x)+Ei x
for any x and y. This rule is valid if y=0. One solution is to create a
Ruleset with two rules, one with and one
without y. A better solution is to use an "optional" pattern variable.
Define rule eirule with a pattern variable ?y to indicate that an
expression may or may not occur.
Apply rule eirule to an integral without this term.
Apply rule eirule to an integral with this term.
Here is one final adornment you will find useful. When matching a pattern
of the form x+y to an expression containing a long sum of the form
a+...+b, there is no way to predict in advance which subset of the sum
matches x and which matches y. Aside from efficiency, this is generally
unimportant since the rule holds for any possible combination of matches
for x and y. In some situations, however, you many want to say which
pattern variable is a sum (or product) of several terms, and which should
match only a single term. To do this, put a prefix colon (":") before the
pattern variable that you want to match mutliple terms. The remaining rules
involve operators u and v.
These definitions tell Axiom that u and v are formal operators to be
used in expressions.
First define myRule with no restrictions on the pattern variables x and y.
Apply myRule to an expression.
Define myOtherRule to match several terms so that the rule gets applied
recursively.
Apply myOtherRule to the same expression
Here are some final remarks on pattern matching. Pattern matching provides
a very useful paradigm for solving certain classes of problems, namely,
those that involve transformations of one form to another and back. However,
it is important to recognize its limitations.
First, pattern matching slows down as the number of rules you have to
apply increases. Thus it is good practice to organize the sets of rules
you use optimally so that irrelevant rules are never included.
Second, careless use of pattern matching can lead to wrong answers. You
should avoid pattern matching to handle hidden algebraic relationships
that can go undetected by other programs. As a simple example, a symbol
such as "J" can easily be used to represent the square root of -1 or some
other important algebraic quantity. Many algorithms branch on whether an
expression is zero or not, then divide by that expression if it is not. If
you fail to simplify an expresison involving powers of J to -1, algorithms
may incorrectly assume an expression is no-zero, take a wrong branch, and
produce a meaningless result.
Pattern matching should also not be used as a substitute for a domain. In
Axiom, objects of one domain are transformed to objects of other domains
using well-defined coerce operations.
Pattern matching should be used on objects that are all of the same type.
Thus if your application can be handled by type
Expression in Axiom and you think you
need pattern matching consider this choice carefully. You may well be
better served by extending an existing domain or by building a new domain
of objects for your application.