Defining equivalence rules for mathematical expressions
This functionality does not run in MATLAB.
Rule(pattern
,replacement
, <conditions
>) Rule(procedure
, <condProc
>)
Rule
is a data type. Each object of Rule
–
a rule – describes the equivalence between mathematical expressions.
The arguments of a rule are two pattern expressions,
that are equivalent, and optional some conditions for the validity
of the equivalence.
Rule can be applied to any expression, and returns an expression
equivalent to the input, or FAIL
.
Additionally, a rule can consist of a procedure that returns
an equivalent expression to a given expression or FAIL
without using
the pattern matcher.
Rules created with Rule
are mainly used to
build a rule base for the new Simplify
. See the documentation of Simplify
and Example 8 for a real application of Rule
. Example 3 shows, how to implement
rewriting rules via Rule
.
All other examples are only given to explain the behavior of rules. In practice, single rules and their manual application is unusual.
There are two kinds of rules: Use the library pattern matcher to determine whether the rule is suitable, or use a user defined procedure to analyze a given expression and return an equivalent expression.
Rule(pattern, replacement, conditions)
defines
a rule that describes the equivalence of the expressions pattern
and replacement
.
When this rule is applied to a given expression ex
,
the pattern matcher is called with
the arguments
match(ex, pattern, Cond = conditions)
and returns a set of replacements S:={var = ex_var,
...}
for each variable var
of pattern
,
and ex_var
is the corresponding subexpression of ex
(see match
for detailed description).
In this case the result of the substitution subs(replacement,
S)
is returned as equivalent expression to ex
.
The call to match
can
also return FAIL
, when ex
doesn't
have the same structure as pattern
. Then the return
value of the rule application is FAIL
, too.
See match
for
the description of valid conditions.
Alternatively, a rule can consist of a procedure that is called
with a given expression, and must return an equivalent expression
or FAIL
. The "pattern matcher" is
not called.
Rule(procedure, condProc)
defines such a
rule that returns an equivalent expression to any given input as return
value of procedure
or FAIL
.
The optional condition condProc
must be a
procedure, too. This procedure is called before the
procedure that produces equivalent expressions, with a given expression ex
.
When the call condProc(ex)
returns TRUE
, then the return
value of the call procedure(ex)
is returned as
the result of the application of the rule, otherwise FAIL
.
With a rule that consists of a procedure, several relations pattern
<=> result
can be expressed. This is mostly more efficient,
than using match
for
each equivalence.
Note:
Rules with expressions as arguments must use identifiers that
are protected from any assignment. Those identifiers must be of the
form 
The first rule represents the simplification sin(X)^2
+ cos(X)^2 = 1
. The first argument of the rule is the expression sin(X)^2
+ cos(X)^2
. Each expression, which has the same structure,
is found by match
,
and the second argument of the rule 1
is returned
as result. There are no conditions for the validity of this equivalence.
The identifiers used for defining the rule are write protected, because
they have names beginning with #
:
r := Rule(sin(`#X`)^2 + cos(`#X`)^2, 1): Rule::apply(r, sin(2*x  1)^2 + cos(2*x  1)^2)
The next expression doesn't have the right form, the application of the rule fails:
Rule::apply(r, sin(2*x  1)^2 + cos(2*x + 1)^2)
The next rule represents the addition theorem sin(X
+ Y) = sin(X)*cos(Y) + sin(Y)*cos(X)
. The first argument
of the rule is the expression sin(X + Y)
. Each
expression that is a call to sin
with
a sum as argument, is identified by match
, and the sum sin(X)*cos(Y)
+ sin(Y)*cos(X)
is returned, where X
and Y
are
replaced by the corresponding parts of the given expression. There
are no conditions for the validity of this equivalence. The second
part of the rule is prevented from evaluation with hold
. The identifiers
used for defining the rule are write protected, because they have
names beginning with #
:
r := Rule(sin(`#X` + `#Y`), hold(sin(`#X`)*cos(`#Y`) + sin(`#Y`)*cos(`#X`))): Rule::apply(r, sin(tan(x) + tan(y)))
The matcher identifies the difference of two expressions a
and b
as
the sum a + b
, therefore also the following example
works:
Rule::apply(r, sin(tan(x)  tan(y)))
We define two rules based on the trigonometric identies sin(x)^{2} = 1  cos(x)^{2} and :
myrules := [Rule(sin(`#X`)^`#n`, (1  cos(`#X`)^2)^(`#n`/2), {`#n` > is(`#n`, Type::Even)}), Rule(tan(`#X`)^`#n`, (1/cos(`#X`)^2  1)^(`#n`/2), {`#n` > is(`#n`, Type::Even)}) ]:
We wish to apply these rules as rewriting rules to various expressions.
We forward Rule::apply
to all subexpressions of
an expression via misc::maprec
.
For convenience, an interface function myrewrite
is
implemented that calls misc::maprec
:
myrewrite:= proc(f, rules) local _rewrite; begin _rewrite:= proc(x) local r, tmp; begin for r in rules do tmp:= Rule::apply(r, x); if tmp <> FAIL then x:= tmp; end; end; return(x) end; misc::maprec(f, TRUE = _rewrite); end:
Now we can call myrewrite(f, myrules)
to
apply the rewriting rules to an expression f
:
f:= tan(x) + sin(2*x)  tan(y)^2*sin(x + 3)^6 + sin(x)^2 * tan(23)^4: myrewrite(f, myrules);
delete myrules, myrewrite, f:
Another rule represents the simplification sin(X) =
0
, which is only true, when X
is an integer
multiple of PI
:
r := Rule(sin(`#X`), 0, {`#X` > is(`#X`/PI, Type::Integer)}): Rule::apply(r, sin(2*x*PI))
In the last call, the argument of sin
doesn't
have the necessary property, so the application of the rule fails.
After an assumption to x
,
the expression has the right form:
assume(x, Type::Integer): Rule::apply(r, sin(2*x*PI))
The next application of the rule checks a constant expression:
Rule::apply(r, sin(2*PI))
Why FAIL
? The problem is, sin
simplifies the constant
input 2*PI
to 0
itself, so the
rule gets 0
as input. However, 0
doesn't
have the necessary form, so FAIL
is returned.
Another rule represents the simplification ln(neg^even*r)
= even*ln(neg) + ln(r)
, which is only true, when neg
is
negative and even
is an even number:
r := Rule(ln(`#Neg`^`#Even`*`#X`), `#Even`*ln(`#Neg`) + ln(`#X`), {`#Neg` > is(`#Neg`, Type::Negative) = TRUE, `#Even` > is(`#Even`, Type::Even) = TRUE}): delete e, n, x: Rule::apply(r, ln(n^e*x))
The rule application fails, because the variables doesn't have the necessary properties.
With an assumption n
should be a negative
variable and e
should be even:
assume(n < 0): assume(e, Type::Even): Rule::apply(r, ln(n^e*x))
This rule represents the application of rewrite
to an expression with the target exp
,
when the expression has subexpressions of type "sin"
or "cos"
.
The first argument of the rule is a procedure that calls rewrite
with any expression
and target exp
and returns an expression equivalent
to the input (because rewrite
does it). The second
argument is a procedure that checks, whether sin
or cos
is
contained in the input expression:
r := Rule(X > rewrite(X, exp), X > has(X, sin) or has(X, cos)): Rule::apply(r, sin(2*x  1)^2 + cos(2*x  1)^2)
The next expression doesn't have sin
or cos
,
so the application of the rule fails:
Rule::apply(r, tan(2*I*x))
This rule represents the application of rewrite
to an expression with several
targets. The first argument of the rule is a procedure that applies rewrite
to the given
expression, with a target depending on the input. This rule doesn't
have a condition procedure:
rewProc := proc(ex) begin rewrite(ex, (if has(ex, exp) then tan elif has(ex, sin) or has(ex, cos) then cot elif has(ex, tan) or has(ex, cot) then sincos else exp end_if)) end_proc: r := Rule(rewProc): Rule::apply(r, exp(2*x))
The rule is applied again to the last result:
Rule::apply(r, %)
The last result should be simplified back to the first expression:
Simplify(%)
The new Simplify
uses a rule base for applying
a lot of rewriting rules for finding the simplest form of any given
expression.
We want to rewrite only some powers and assume that all used variables are real (without using properties).
The list PowerRules
consists of several rules.
The procedure powerRules
returns all this rules
in a list.
Because of better readability, the names of the used identifiers are short and not protected names:
PowerRules := [Rule(A^m*A^n, hold(A^(m + n))), Rule(A^m/A^n, hold(A^(m  n))), Rule(A^n*B^n, hold((A*B)^n)), Rule(A^n/B^n, hold((A/B)^n)), Rule(A^n/B^n, hold((B/A)^n)), Rule((A^m)^n, hold(A^(m*n)))]: powerRules := proc() begin PowerRules end_proc:
Simplify
is
called with the option SelectRules
, and expects
a procedure that returns a list of rules, applicable to a given expression.
In this case, all of the rules are returned in every case.
Simplify
applies
all rules to a given expression and also to rewritten results, and
tries to find the easiest form of the expression with respect to the
default valuation procedure Simplify::complexity
.
Because of the argument SelectRules = powerRules
,
only the given rules are used by Simplify
:
Simplify(T^(1/2)*(R*T)^(1/2), SelectRules = powerRules)
Other expressions cannot be simplified with the same rule base:
Simplify(sin(x)^2 + cos(x)^2, SelectRules = powerRules)
delete r, x, powerRules, PowerRules:

A MuPAD^{®} expression; all identifiers are used as pattern variables for the pattern matcher 

A MuPAD expression with the same identifiers, as 

A set (of type 

A MuPAD procedure that is called with an expression and
must return an equivalent expression or 

A procedure that is called with an expression before 