The Mathematica Journal
Current Issue
Tricks of the Trade
In and Out
Riemann Surfaces IIc
The Mathematica Programmer
New Products
New Publications
News Bulletins
Editor's Pick
Write Us
About the Journal
Staff and Contributors
Back Issues
Download This Issue

Controlling Evaluation

One of the characteristics of Mathematica and other computer algebra systems that educators sometimes find frustrating is the fact that these systems generate results without showing intermediate steps. Leibniz addresses this problem by giving the user very fine control over just what part of an expression gets modified and how drastically the expression gets modified in a single step.

By selecting only a portion of an expression to work on, or by dragging and dropping within a larger expression, the user gets to focus the calculation. Leibniz supports this focusing behavior by sending just the relevant part of an expression to the kernel. When the result comes back, its gets swapped into place in the larger expression it came from, and that expression is then formatted and displayed.

Even with this focusing behavior, special care is needed to avoid unwanted evaluation. Consider our seemingly innocent example of a rule for moving a term from one side of an equation to the other.

This seems straightforward enough, but what happens when, say, b is an integral? The user's intention will be to subtract that integral from both sides without evaluating it, but Mathematica's default evaluation mechanisms will cause b to be evaluated, thus making the integral disappear after subtracting it from both sides. One obvious solution would be to give LZShuffle the attribute HoldAll and replace the result above as follows.

This solves the problem of the unwanted evaluation, but then causes the expression to fail to evaluate correctly when the user wants it to. For example, if c were 3 and b were 2, the user would expect to be able to subtract 2 from both sides of the original equation giving a new right-hand side of 1. However, with both c and b held as shown here, the user would see a new right-hand side of the unevaluated expression .

The lack of control offered by the standard evaluation mechanisms and the Hold mechanism make it necessary to find a more refined strategy for controlling evaluation.

The first element of this strategy is to force every expression that the system works with to be a purely symbolic expression. This is done by replacing all conventional mathematical functions such as Plus, Integrate, D, and so forth with purely symbolic analogs called LZPlus, LZIntegrate, LZD, and so on. Thus, rather than rendering the expression a+b as Plus[a,b], the Leibniz parser passes the expression to the kernel as LZPlus[a,b].

With a little bit of care, we can do pattern matching with these new expressions just as easily as we could with more conventional expressions. For example, by giving the function LZPlus the attribute OrderLess, we can write pattern matching rules for LZPlus without regard to the ordering of terms involved.

Because none of these new functions come with evaluation rules, everything gets held by default. To release the implicit hold, LZCompute uses a couple of mechanisms. The first mechanism releases the hold partially so that the usual evaluation rules can perform obvious algebraic simplifications.

Thus, the LZShuffle rule shown above becomes the following.

This solves both of the problems described earlier. If c is 3 and b is 2, the right-hand side will correctly evaluate to 1. If b is an integral, LZPEval will leave it unevaluated in the LZIntegrate form.

The second evaluation mechanism used is a complete evaluation mechanism, which converts every function in an expression from its LZ form to its conventional form. This mechanism is implemented via a function called LZEval.

The LZRelease function maps each individual LZ function to its conventional equivalent. For example, here is part of the definition of LZRelease.

This hold and release mechanism provides other benefits, as well. Because LZRelease has to map the held forms to their conventional equivalents, we can take advantage of the need to do that mapping to provide more sophisticated handling for certain special cases. The best example of this is matrix multiplication. Experienced Mathematica programmers know that if we try to compute the product of a two by two matrix and a column vector by naively writing

we get the incorrect result

If we remember to use Dot instead of Times, everything works out just fine. Leibniz solves this problem for the user by using LZTimes for everything that looks like a product and then relying on the release mechanism to sort out the details. LZRelease uses some simple and straightforward pattern matching to ensure that LZTimes maps to the appropriate equivalent.

Copyright © 2002 Wolfram Media, Inc. All rights reserved.

[Article Index][Prev Page][Next Page]