[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12. Algebra

This section covers the Calc features that help you work with algebraic formulas. First, the general sub-formula selection mechanism is described; this works in conjunction with any Calc commands. Then, commands for specific algebraic operations are described. Finally, the flexible rewrite rule mechanism is discussed.

The algebraic commands use the a key prefix; selection commands use the j (for "just a letter that wasn't used for anything else") prefix.

See section 7.2 Editing Stack Entries, to see how to manipulate formulas using regular Emacs editing commands.

When doing algebraic work, you may find several of the Calculator's modes to be helpful, including algebraic-simplification mode (m A) or no-simplification mode (m O), algebraic-entry mode (m a), fraction mode (m f), and symbolic mode (m s). See section 8. Mode Settings, for discussions of these modes. You may also wish to select "big" display mode (d B). See section 8.8.1 Normal Language Modes.

12.1 Selecting Sub-Formulas  
12.2 Algebraic Manipulation  
12.3 Simplifying Formulas  
12.4 Polynomials  
12.5 Calculus  
12.6 Solving Equations  
12.7 Numerical Solutions  
12.8 Curve Fitting  
12.9 Summations  
12.10 Logical Operations  
12.11 Rewrite Rules  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1 Selecting Sub-Formulas

When working with an algebraic formula it is often necessary to manipulate a portion of the formula rather than the formula as a whole. Calc allows you to "select" a portion of any formula on the stack. Commands which would normally operate on that stack entry will now operate only on the sub-formula, leaving the surrounding part of the stack entry alone.

One common non-algebraic use for selection involves vectors. To work on one element of a vector in-place, simply select that element as a "sub-formula" of the vector.

12.1.1 Making Selections  
12.1.2 Changing Selections  
12.1.3 Displaying Selections  
12.1.4 Operating on Selections  
12.1.5 Rearranging Formulas using Selections  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1.1 Making Selections

To select a sub-formula, move the Emacs cursor to any character in that sub-formula, and press j s (calc-select-here). Calc will highlight the smallest portion of the formula that contains that character. By default the sub-formula is highlighted by blanking out all of the rest of the formula with dots. Selection works in any display mode but is perhaps easiest in "big" (d B) mode. Suppose you enter the following formula:

           3    ___
    (a + b)  + V c
1:  ---------------
        2 x + 1

(by typing ' ((a+b)^3 + sqrt(c)) / (2x+1)). If you move the cursor to the letter `b' and press j s, the display changes to

           .    ...
    .. . b.  . . .
1*  ...............
        . . . .

Every character not part of the sub-formula `b' has been changed to a dot. The `*' next to the line number is to remind you that the formula has a portion of it selected. (In this case, it's very obvious, but it might not always be. If Embedded Mode is enabled, the word `Sel' also appears in the mode line because the stack may not be visible. see section 18. Embedded Mode.)

If you had instead placed the cursor on the parenthesis immediately to the right of the `b', the selection would have been:

           .    ...
    (a + b)  . . .
1*  ...............
        . . . .

The portion selected is always large enough to be considered a complete formula all by itself, so selecting the parenthesis selects the whole formula that it encloses. Putting the cursor on the `+' sign would have had the same effect.

(Strictly speaking, the Emacs cursor is really the manifestation of the Emacs "point," which is a position between two characters in the buffer. So purists would say that Calc selects the smallest sub-formula which contains the character to the right of "point.")

If you supply a numeric prefix argument n, the selection is expanded to the nth enclosing sub-formula. Thus, positioning the cursor on the `b' and typing C-u 1 j s will select `a + b'; typing C-u 2 j s will select `(a + b)^3', and so on.

If the cursor is not on any part of the formula, or if you give a numeric prefix that is too large, the entire formula is selected.

If the cursor is on the `.' line that marks the top of the stack (i.e., its normal "rest position"), this command selects the entire formula at stack level 1. Most selection commands similarly operate on the formula at the top of the stack if you haven't positioned the cursor on any stack entry.

The j a (calc-select-additional) command enlarges the current selection to encompass the cursor. To select the smallest sub-formula defined by two different points, move to the first and press j s, then move to the other and press j a. This is roughly analogous to using C-@ (set-mark-command) to select the two ends of a region of text during normal Emacs editing.

The j o (calc-select-once) command selects a formula in exactly the same way as j s, except that the selection will last only as long as the next command that uses it. For example, j o 1 + is a handy way to add one to the sub-formula indicated by the cursor.

(A somewhat more precise definition: The j o command sets a flag such that the next command involving selected stack entries will clear the selections on those stack entries afterwards. All other selection commands except j a and j O clear this flag.)

The j S (calc-select-here-maybe) and j O (calc-select-once-maybe) commands are equivalent to j s and j o, respectively, except that if the formula already has a selection they have no effect. This is analogous to the behavior of some commands such as j r (calc-rewrite-selection; see section 12.11.9 Selections with Rewrite Rules) and is mainly intended to be used in keyboard macros that implement your own selection-oriented commands.

Selection of sub-formulas normally treats associative terms like `a + b - c + d' and `x * y * z' as single levels of the formula. If you place the cursor anywhere inside `a + b - c + d' except on one of the variable names and use j s, you will select the entire four-term sum.

The j b (calc-break-selections) command controls a mode in which the "deep structure" of these associative formulas shows through. Calc actually stores the above formulas as `((a + b) - c) + d' and `x * (y * z)'. (Note that for certain obscure reasons, Calc treats multiplication as right-associative.) Once you have enabled j b mode, selecting with the cursor on the `-' sign would only select the `a + b - c' portion, which makes sense when the deep structure of the sum is considered. There is no way to select the `b - c + d' portion; although this might initially look like just as legitimate a sub-formula as `a + b - c', the deep structure shows that it isn't. The d U command can be used to view the deep structure of any formula (see section 8.8.1 Normal Language Modes).

When j b mode has not been enabled, the deep structure is generally hidden by the selection commands--what you see is what you get.

The j u (calc-unselect) command unselects the formula that the cursor is on. If there was no selection in the formula, this command has no effect. With a numeric prefix argument, it unselects the nth stack element rather than using the cursor position.

The j c (calc-clear-selections) command unselects all stack elements.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1.2 Changing Selections

Once you have selected a sub-formula, you can expand it using the j m (calc-select-more) command. If `a + b' is selected, pressing j m repeatedly works as follows:

           3    ...                3    ___                3    ___
    (a + b)  . . .          (a + b)  + V c          (a + b)  + V c
1*  ...............     1*  ...............     1*  ---------------
        . . . .                 . . . .                 2 x + 1

In the last example, the entire formula is selected. This is roughly the same as having no selection at all, but because there are subtle differences the `*' character is still there on the line number.

With a numeric prefix argument n, j m expands n times (or until the entire formula is selected). Note that j s with argument n is equivalent to plain j s followed by j m with argument n. If j m is used when there is no current selection, it is equivalent to j s.

Even though j m does not explicitly use the location of the cursor within the formula, it nevertheless uses the cursor to determine which stack element to operate on. As usual, j m when the cursor is not on any stack element operates on the top stack element.

The j l (calc-select-less) command reduces the current selection around the cursor position. That is, it selects the immediate sub-formula of the current selection which contains the cursor, the opposite of j m. If the cursor is not inside the current selection, the command de-selects the formula.

The j 1 through j 9 (calc-select-part) commands select the nth sub-formula of the current selection. They are like j l (calc-select-less) except they use counting rather than the cursor position to decide which sub-formula to select. For example, if the current selection is a + b + c or f(a, b, c) or [a, b, c], then j 1 selects `a', j 2 selects `b', and j 3 selects `c'; in each of these cases, j 4 through j 9 would be errors.

If there is no current selection, j 1 through j 9 select the nth top-level sub-formula. (In other words, they act as if the entire stack entry were selected first.) To select the nth sub-formula where n is greater than nine, you must instead invoke j 1 with n as a numeric prefix argument.

The j n (calc-select-next) and j p (calc-select-previous) commands change the current selection to the next or previous sub-formula at the same level. For example, if `b' is selected in `2 + a*b*c + x', then j n selects `c'. Further j n commands would be in error because, even though there is something to the right of `c' (namely, `x'), it is not at the same level; in this case, it is not a term of the same product as `b' and `c'. However, j m (to select the whole product `a*b*c' as a term of the sum) followed by j n would successfully select the `x'.

Similarly, j p moves the selection from the `b' in this sample formula to the `a'. Both commands accept numeric prefix arguments to move several steps at a time.

It is interesting to compare Calc's selection commands with the Emacs Info system's commands for navigating through hierarchically organized documentation. Calc's j n command is completely analogous to Info's n command. Likewise, j p maps to p, j 2 maps to 2, and Info's u is like j m. (Note that j u stands for calc-unselect, not "up".) The Info m command is somewhat similar to Calc's j s and j l; in each case, you can jump directly to a sub-component of the hierarchy simply by pointing to it with the cursor.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1.3 Displaying Selections

The j d (calc-show-selections) command controls how selected sub-formulas are displayed. One of the alternatives is illustrated in the above examples; if we press j d we switch to the other style in which the selected portion itself is obscured by `#' signs:

           3    ...                  #    ___
    (a + b)  . . .            ## # ##  + V c
1*  ...............       1*  ---------------
        . . . .                   2 x + 1

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1.4 Operating on Selections

Once a selection is made, all Calc commands that manipulate items on the stack will operate on the selected portions of the items instead. (Note that several stack elements may have selections at once, though there can be only one selection at a time in any given stack element.)

The j e (calc-enable-selections) command disables the effect that selections have on Calc commands. The current selections still exist, but Calc commands operate on whole stack elements anyway. This mode can be identified by the fact that the `*' markers on the line numbers are gone, even though selections are visible. To reactivate the selections, press j e again.

To extract a sub-formula as a new formula, simply select the sub-formula and press RET. This normally duplicates the top stack element; here it duplicates only the selected portion of that element.

To replace a sub-formula with something different, you can enter the new value onto the stack and press TAB. This normally exchanges the top two stack elements; here it swaps the value you entered into the selected portion of the formula, returning the old selected portion to the top of the stack.

           3    ...                    ...                    ___
    (a + b)  . . .           17 x y . . .           17 x y + V c
2*  ...............      2*  .............      2:  -------------
        . . . .                 . . . .                2 x + 1

                                    3                      3
1:  17 x y               1:  (a + b)            1:  (a + b)

In this example we select a sub-formula of our original example, enter a new formula, TAB it into place, then deselect to see the complete, edited formula.

If you want to swap whole formulas around even though they contain selections, just use j e before and after.

The j ' (calc-enter-selection) command is another way to replace a selected sub-formula. This command does an algebraic entry just like the regular ' key. When you press RET, the formula you type replaces the original selection. You can use the `$' symbol in the formula to refer to the original selection. If there is no selection in the formula under the cursor, the cursor is used to make a temporary selection for the purposes of the command. Thus, to change a term of a formula, all you have to do is move the Emacs cursor to that term and press j '.

The j ` (calc-edit-selection) command is a similar analogue of the ` (calc-edit) command. It edits the selected sub-formula in a separate buffer. If there is no selection, it edits the sub-formula indicated by the cursor.

To delete a sub-formula, press DEL. This generally replaces the sub-formula with the constant zero, but in a few suitable contexts it uses the constant one instead. The DEL key automatically deselects and re-simplifies the entire formula afterwards. Thus:

    17 x y + # #          17 x y         17 # y          17 y
1*  -------------     1:  -------    1*  -------    1:  -------
       2 x + 1            2 x + 1        2 x + 1        2 x + 1

In this example, we first delete the `sqrt(c)' term; Calc accomplishes this by replacing `sqrt(c)' with zero and resimplifying. We then delete the x in the numerator; since this is part of a product, Calc replaces it with `1' and resimplifies.

If you select an element of a vector and press DEL, that element is deleted from the vector. If you delete one side of an equation or inequality, only the opposite side remains.

The j DEL (calc-del-selection) command is like DEL but with the auto-selecting behavior of j ' and j `. It deletes the selected portion of the formula indicated by the cursor, or, in the absence of a selection, it deletes the sub-formula indicated by the cursor position.

(There is also an auto-selecting j RET (calc-copy-selection) command.)

Normal arithmetic operations also apply to sub-formulas. Here we select the denominator, press 5 - to subtract five from the denominator, press n to negate the denominator, then press Q to take the square root.

     .. .           .. .           .. .             .. .
1*  .......    1*  .......    1*  .......    1*  ..........
    2 x + 1        2 x - 4        4 - 2 x         _________
                                                 V 4 - 2 x

Certain types of operations on selections are not allowed. For example, for an arithmetic function like - no more than one of the arguments may be a selected sub-formula. (As the above example shows, the result of the subtraction is spliced back into the argument which had the selection; if there were more than one selection involved, this would not be well-defined.) If you try to subtract two selections, the command will abort with an error message.

Operations on sub-formulas sometimes leave the formula as a whole in an "un-natural" state. Consider negating the `2 x' term of our sample formula by selecting it and pressing n (calc-change-sign).

       .. .                .. .
1*  ..........      1*  ...........
     .........           ..........
    . . . 2 x           . . . -2 x

Unselecting the sub-formula reveals that the minus sign, which would normally have cancelled out with the subtraction automatically, has not been able to do so because the subtraction was not part of the selected portion. Pressing = (calc-evaluate) or doing any other mathematical operation on the whole formula will cause it to be simplified.

       17 y                17 y
1:  -----------     1:  ----------
     __________          _________
    V 4 - -2 x          V 4 + 2 x

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.1.5 Rearranging Formulas using Selections

The j R (calc-commute-right) command moves the selected sub-formula to the right in its surrounding formula. Generally the selection is one term of a sum or product; the sum or product is rearranged according to the commutative laws of algebra.

As with j ' and j DEL, the term under the cursor is used if there is no selection in the current formula. All commands described in this section share this property. In this example, we place the cursor on the `a' and type j R, then repeat.

1:  a + b - c          1:  b + a - c          1:  b - c + a

Note that in the final step above, the `a' is switched with the `c' but the signs are adjusted accordingly. When moving terms of sums and products, j R will never change the mathematical meaning of the formula.

The selected term may also be an element of a vector or an argument of a function. The term is exchanged with the one to its right. In this case, the "meaning" of the vector or function may of course be drastically changed.

1:  [a, b, c]          1:  [b, a, c]          1:  [b, c, a]

1:  f(a, b, c)         1:  f(b, a, c)         1:  f(b, c, a)

The j L (calc-commute-left) command is like j R except that it swaps the selected term with the one to its left.

With numeric prefix arguments, these commands move the selected term several steps at a time. It is an error to try to move a term left or right past the end of its enclosing formula. With numeric prefix arguments of zero, these commands move the selected term as far as possible in the given direction.

The j D (calc-sel-distribute) command mixes the selected sum or product into the surrounding formula using the distributive law. For example, in `a * (b - c)' with the `b - c' selected, the result is `a b - a c'. This also distributes products or quotients into surrounding powers, and can also do transformations like `exp(a + b)' to `exp(a) exp(b)', where `a + b' is the selected term, and `ln(a ^ b)' to `ln(a) b', where `a ^ b' is the selected term.

For multiple-term sums or products, j D takes off one term at a time: `a * (b + c - d)' goes to `a * (c - d) + a b' with the `c - d' selected so that you can type j D repeatedly to expand completely. The j D command allows a numeric prefix argument which specifies the maximum number of times to expand at once; the default is one time only.

The j D command is implemented using rewrite rules. See section 12.11.9 Selections with Rewrite Rules. The rules are stored in the Calc variable DistribRules. A convenient way to view these rules is to use s e (calc-edit-variable) which displays and edits the stored value of a variable. Press M-# M-# to return from editing mode; be careful not to make any actual changes or else you will affect the behavior of future j D commands!

To extend j D to handle new cases, just edit DistribRules as described above. You can then use the s p command to save this variable's value permanently for future Calc sessions. See section 14.3 Other Operations on Variables.

The j M (calc-sel-merge) command is the complement of j D; given `a b - a c' with either `a b' or `a c' selected, the result is `a * (b - c)'. Once again, j M can also merge calls to functions like exp and ln; examine the variable MergeRules to see all the relevant rules.

The j C (calc-sel-commute) command swaps the arguments of the selected sum, product, or equation. It always behaves as if j b mode were in effect, i.e., the sum `a + b + c' is treated as the nested sums `(a + b) + c' by this command. If you put the cursor on the first `+', the result is `(b + a) + c'; if you put the cursor on the second `+', the result is `c + (a + b)' (which the default simplifications will rearrange to `(c + a) + b'). The relevant rules are stored in the variable CommuteRules.

You may need to turn default simplifications off (with the m O command) in order to get the full benefit of j C. For example, commuting `a - b' produces `-b + a', but the default simplifications will "simplify" this right back to `a - b' if you don't turn them off. The same is true of some of the other manipulations described in this section.

The j N (calc-sel-negate) command replaces the selected term with the negative of that term, then adjusts the surrounding formula in order to preserve the meaning. For example, given `exp(a - b)' where `a - b' is selected, the result is `1 / exp(b - a)'. By contrast, selecting a term and using the regular n (calc-change-sign) command negates the term without adjusting the surroundings, thus changing the meaning of the formula as a whole. The rules variable is NegateRules.

The j & (calc-sel-invert) command is similar to j N except it takes the reciprocal of the selected term. For example, given `a - ln(b)' with `b' selected, the result is `a + ln(1/b)'. The rules variable is InvertRules.

The j E (calc-sel-jump-equals) command moves the selected term from one side of an equation to the other. Given `a + b = c + d' with `c' selected, the result is `a + b - c = d'. This command also works if the selected term is part of a `*', `/', or `^' formula. The relevant rules variable is JumpRules.

The j I (calc-sel-isolate) command isolates the selected term on its side of an equation. It uses the a S (calc-solve-for) command to solve the equation, and the Hyperbolic flag affects it in the same way. See section 12.6 Solving Equations. When it applies, j I is often easier to use than j E. It understands more rules of algebra, and works for inequalities as well as equations.

The j * (calc-sel-mult-both-sides) command prompts for a formula using algebraic entry, then multiplies both sides of the selected quotient or equation by that formula. It simplifies each side with a s (calc-simplify) before re-forming the quotient or equation. You can suppress this simplification by providing any numeric prefix argument. There is also a j / (calc-sel-div-both-sides) which is similar to j * but dividing instead of multiplying by the factor you enter.

As a special feature, if the numerator of the quotient is 1, then the denominator is expanded at the top level using the distributive law (i.e., using the C-u -1 a x command). Suppose the formula on the stack is `1 / (sqrt(a) + 1)', and you wish to eliminate the square root in the denominator by multiplying both sides by `sqrt(a) - 1'. Calc's default simplifications would change the result `(sqrt(a) - 1) / (sqrt(a) - 1) (sqrt(a) + 1)' right back to the original form by cancellation; Calc expands the denominator to `sqrt(a) (sqrt(a) - 1) + sqrt(a) - 1' to prevent this. (You would now want to use an a x command to expand the rest of the way, whereupon the denominator would cancel out to the desired form, `a - 1'.) When the numerator is not 1, this initial expansion is not necessary because Calc's default simplifications will not notice the potential cancellation.

If the selection is an inequality, j * and j / will accept any factor, but will warn unless they can prove the factor is either positive or negative. (In the latter case the direction of the inequality will be switched appropriately.) See section 8.6 Declarations, for ways to inform Calc that a given variable is positive or negative. If Calc can't tell for sure what the sign of the factor will be, it will assume it is positive and display a warning message.

For selections that are not quotients, equations, or inequalities, these commands pull out a multiplicative factor: They divide (or multiply) by the entered formula, simplify, then multiply (or divide) back by the formula.

The j + (calc-sel-add-both-sides) and j - (calc-sel-sub-both-sides) commands analogously add to or subtract from both sides of an equation or inequality. For other types of selections, they extract an additive factor. A numeric prefix argument suppresses simplification of the intermediate results.

The j U (calc-sel-unpack) command replaces the selected function call with its argument. For example, given `a + sin(x^2)' with `sin(x^2)' selected, the result is `a + x^2'. (The `x^2' will remain selected; if you wanted to change the sin to cos, just press C now to take the cosine of the selected part.)

The j v (calc-sel-evaluate) command performs the normal default simplifications on the selected sub-formula. These are the simplifications that are normally done automatically on all results, but which may have been partially inhibited by previous selection-related operations, or turned off altogether by the m O command. This command is just an auto-selecting version of the a v command (see section 12.2 Algebraic Manipulation).

With a numeric prefix argument of 2, C-u 2 j v applies the a s (calc-simplify) command to the selected sub-formula. With a prefix argument of 3 or more, e.g., C-u j v applies the a e (calc-simplify-extended) command. See section 12.3 Simplifying Formulas. With a negative prefix argument it simplifies at the top level only, just as with a v. Here the "top" level refers to the top level of the selected sub-formula.

The j " (calc-sel-expand-formula) command is to a " (see section 12.2 Algebraic Manipulation) what j v is to a v.

You can use the j r (calc-rewrite-selection) command to define other algebraic operations on sub-formulas. See section 12.11 Rewrite Rules.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.2 Algebraic Manipulation

The commands in this section perform general-purpose algebraic manipulations. They work on the whole formula at the top of the stack (unless, of course, you have made a selection in that formula).

Many algebra commands prompt for a variable name or formula. If you answer the prompt with a blank line, the variable or formula is taken from top-of-stack, and the normal argument for the command is taken from the second-to-top stack level.

The a v (calc-alg-evaluate) command performs the normal default simplifications on a formula; for example, `a - -b' is changed to `a + b'. These simplifications are normally done automatically on all Calc results, so this command is useful only if you have turned default simplifications off with an m O command. See section 8.5 Simplification Modes.

It is often more convenient to type =, which is like a v but which also substitutes stored values for variables in the formula. Use a v if you want the variables to ignore their stored values.

If you give a numeric prefix argument of 2 to a v, it simplifies as if in algebraic simplification mode. This is equivalent to typing a s; see section 12.3 Simplifying Formulas. If you give a numeric prefix of 3 or more, it uses extended simplification mode (a e).

If you give a negative prefix argument -1, -2, or -3, it simplifies in the corresponding mode but only works on the top-level function call of the formula. For example, `(2 + 3) * (2 + 3)' will simplify to `(2 + 3)^2', without simplifying the sub-formulas `2 + 3'. As another example, typing V R + to sum the vector `[1, 2, 3, 4]' produces the formula `reduce(add, [1, 2, 3, 4])' in no-simplify mode. Using a v will evaluate this all the way to 10; using C-u - a v will evaluate it only to `1 + 2 + 3 + 4'. (See section 11.8 Reducing and Mapping Vectors.)

The = command corresponds to the evalv function, and the related N command, which is like = but temporarily disables symbolic (m s) mode during the evaluation, corresponds to the evalvn function. (These commands interpret their prefix arguments differently than a v; = treats the prefix as the number of stack elements to evaluate at once, and N treats it as a temporary different working precision.)

The evalvn function can take an alternate working precision as an optional second argument. This argument can be either an integer, to set the precision absolutely, or a vector containing a single integer, to adjust the precision relative to the current precision. Note that evalvn with a larger than current precision will do the calculation at this higher precision, but the result will as usual be rounded back down to the current precision afterward. For example, `evalvn(pi - 3.1415)' at a precision of 12 will return `9.265359e-5'; `evalvn(pi - 3.1415, 30)' will return `9.26535897932e-5' (computing a 25-digit result which is then rounded down to 12); and `evalvn(pi - 3.1415, [-2])' will return `9.2654e-5'.

The a " (calc-expand-formula) command expands functions into their defining formulas wherever possible. For example, `deg(x^2)' is changed to `180 x^2 / pi'. Most functions, like sin and gcd, are not defined by simple formulas and so are unaffected by this command. One important class of functions which can be expanded is the user-defined functions created by the Z F command. See section 19.4 Programming with Formulas. Other functions which a " can expand include the probability distribution functions, most of the financial functions, and the hyperbolic and inverse hyperbolic functions. A numeric prefix argument affects a " in the same way as it does a v: A positive argument expands all functions in the formula and then simplifies in various ways; a negative argument expands and simplifies only the top-level function call.

The a M (calc-map-equation) [mapeq] command applies a given function or operator to one or more equations. It is analogous to V M, which operates on vectors instead of equations. see section 11.8 Reducing and Mapping Vectors. For example, a M S changes `x = y+1' to `sin(x) = sin(y+1)', and a M + with `x = y+1' and 6 on the stack produces `x+6 = y+7'. With two equations on the stack, a M + would add the lefthand sides together and the righthand sides together to get the two respective sides of a new equation.

Mapping also works on inequalities. Mapping two similar inequalities produces another inequality of the same type. Mapping an inequality with an equation produces an inequality of the same type. Mapping a `<=' with a `<' or `!=' (not-equal) produces a `<'. If inequalities with opposite direction (e.g., `<' and `>') are mapped, the direction of the second inequality is reversed to match the first: Using a M + on `a < b' and `a > 2' reverses the latter to get `2 < a', which then allows the combination `a + 2 < b + a', which the a s command can then simplify to get `2 < b'.

Using a M *, a M /, a M n, or a M & to negate or invert an inequality will reverse the direction of the inequality. Other adjustments to inequalities are not done automatically; a M S will change `x < y' to `sin(x) < sin(y)' even though this is not true for all values of the variables.

With the Hyperbolic flag, H a M [mapeqp] does a plain mapping operation without reversing the direction of any inequalities. Thus, H a M & would change x > 2 to 1/x > 0.5. (This change is mathematically incorrect, but perhaps you were fixing an inequality which was already incorrect.)

With the Inverse flag, I a M [mapeqr] always reverses the direction of the inequality. You might use I a M C to change `x < y' to `cos(x) > cos(y)' if you know you are working with small positive angles.

The a b (calc-substitute) [subst] command substitutes all occurrences of some variable or sub-expression of an expression with a new sub-expression. For example, substituting `sin(x)' with `cos(y)' in `2 sin(x)^2 + x sin(x) + sin(2 x)' produces `2 cos(y)^2 + x cos(y) + sin(2 x)'. Note that this is a purely structural substitution; the lone `x' and the `sin(2 x)' stayed the same because they did not look like `sin(x)'. See section 12.11 Rewrite Rules, for a more general method for doing substitutions.

The a b command normally prompts for two formulas, the old one and the new one. If you enter a blank line for the first prompt, all three arguments are taken from the stack (new, then old, then target expression). If you type an old formula but then enter a blank line for the new one, the new formula is taken from top-of-stack and the target from second-to-top. If you answer both prompts, the target is taken from top-of-stack as usual.

Note that a b has no understanding of commutativity or associativity. The pattern `x+y' will not match the formula `y+x'. Also, `y+z' will not match inside the formula `x+y+z' because the `+' operator is left-associative, so the "deep structure" of that formula is `(x+y) + z'. Use d U (calc-unformatted-language) mode to see the true structure of a formula. The rewrite rule mechanism, discussed later, does not have these limitations.

As an algebraic function, subst takes three arguments: Target expression, old, new. Note that subst is always evaluated immediately, even if its arguments are variables, so if you wish to put a call to subst onto the stack you must turn the default simplifications off first (with m O).

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.3 Simplifying Formulas

The a s (calc-simplify) [simplify] command applies various algebraic rules to simplify a formula. This includes rules which are not part of the default simplifications because they may be too slow to apply all the time, or may not be desirable all of the time. For example, non-adjacent terms of sums are combined, as in `a + b + 2 a' to `b + 3 a', and some formulas like `sin(arcsin(x))' are simplified to `x'.

The sections below describe all the various kinds of algebraic simplifications Calc provides in full detail. None of Calc's simplification commands are designed to pull rabbits out of hats; they simply apply certain specific rules to put formulas into less redundant or more pleasing forms. Serious algebra in Calc must be done manually, usually with a combination of selections and rewrite rules. See section 12.1.5 Rearranging Formulas using Selections. See section 12.11 Rewrite Rules.

See section 8.5 Simplification Modes, for commands to control what level of simplification occurs automatically. Normally only the "default simplifications" occur.

12.3.1 Default Simplifications  
12.3.2 Algebraic Simplifications  
12.3.3 "Unsafe" Simplifications  
12.3.4 Simplification of Units  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.3.1 Default Simplifications

This section describes the "default simplifications," those which are normally applied to all results. For example, if you enter the variable x on the stack twice and push +, Calc's default simplifications automatically change x + x to 2 x.

The m O command turns off the default simplifications, so that x + x will remain in this form unless you give an explicit "simplify" command like = or a v. See section 12.2 Algebraic Manipulation. The m D command turns the default simplifications back on.

The most basic default simplification is the evaluation of functions. For example, 2 + 3 is evaluated to 5, and sqrt(9) is evaluated to 3. Evaluation does not occur if the arguments to a function are somehow of the wrong type (tan([2,3,4]), range (tan(90)), or number (tan(3,5)), or if the function name is not recognized (f(5)), or if "symbolic" mode (see section 8.4.5 Symbolic Mode) prevents evaluation (sqrt(2)).

Calc simplifies (evaluates) the arguments to a function before it simplifies the function itself. Thus sqrt(5+4) is simplified to sqrt(9) before the sqrt function itself is applied. There are very few exceptions to this rule: quote, lambda, and condition (the :: operator) do not evaluate their arguments, if (the ? : operator) does not evaluate all of its arguments, and evalto does not evaluate its lefthand argument.

Most commands apply the default simplifications to all arguments they take from the stack, perform a particular operation, then simplify the result before pushing it back on the stack. In the common special case of regular arithmetic commands like + and Q [sqrt], the arguments are simply popped from the stack and collected into a suitable function call, which is then simplified (the arguments being simplified first as part of the process, as described above).

The default simplifications are too numerous to describe completely here, but this section will describe the ones that apply to the major arithmetic operators. This list will be rather technical in nature, and will probably be interesting to you only if you are a serious user of Calc's algebra facilities.

As well as the simplifications described here, if you have stored any rewrite rules in the variable EvalRules then these rules will also be applied before any built-in default simplifications. See section 12.11.11 Automatic Rewrites, for details.

And now, on with the default simplifications:

Arithmetic operators like + and * always take two arguments in Calc's internal form. Sums and products of three or more terms are arranged by the associative law of algebra into a left-associative form for sums, ((a + b) + c) + d, and a right-associative form for products, a * (b * (c * d)). Formulas like (a + b) + (c + d) are rearranged to left-associative form, though this rarely matters since Calc's algebra commands are designed to hide the inner structure of sums and products as much as possible. Sums and products in their proper associative form will be written without parentheses in the examples below.

Sums and products are not rearranged according to the commutative law (a + b to b + a) except in a few special cases described below. Some algebra programs always rearrange terms into a canonical order, which enables them to see that a b + b a can be simplified to 2 a b. Calc assumes you have put the terms into the order you want and generally leaves that order alone, with the consequence that formulas like the above will only be simplified if you explicitly give the a s command. See section 12.3.2 Algebraic Simplifications.

Differences a - b are treated like sums a + (-b) for purposes of simplification; one of the default simplifications is to rewrite a + (-b) or (-b) + a, where -b represents a "negative-looking" term, into a - b form. "Negative-looking" means negative numbers, negated formulas like -x, and products or quotients in which either term is negative-looking.

Other simplifications involving negation are -(-x) to x; -(a b) or -(a/b) where either a or b is negative-looking, simplified by negating that term, or else where a or b is any number, by negating that number; -(a + b) to -a - b, and -(b - a) to a - b. (This, and rewriting (-b) + a to a - b, are the only cases where the order of terms in a sum is changed by the default simplifications.)

The distributive law is used to simplify sums in some cases: a x + b x to (a + b) x, where a represents a number or an implicit 1 or -1 (as in x or -x) and similarly for b. Use the a c, a f, or j M commands to merge sums with non-numeric coefficients using the distributive law.

The distributive law is only used for sums of two terms, or for adjacent terms in a larger sum. Thus a + b + b + c is simplified to a + 2 b + c, but a + b + c + b is not simplified. The reason is that comparing all terms of a sum with one another would require time proportional to the square of the number of terms; Calc relegates potentially slow operations like this to commands that have to be invoked explicitly, like a s.

Finally, a + 0 and 0 + a are simplified to a. A consequence of the above rules is that 0 - a is simplified to -a.

The products 1 a and a 1 are simplified to a; (-1) a and a (-1) are simplified to -a; 0 a and a 0 are simplified to 0, except that in matrix mode where a is not provably scalar the result is the generic zero matrix `idn(0)', and that if a is infinite the result is `nan'.

Also, (-a) b and a (-b) are simplified to -(a b), where this occurs for negated formulas but not for regular negative numbers.

Products are commuted only to move numbers to the front: a b 2 is commuted to 2 a b.

The product a (b + c) is distributed over the sum only if a and at least one of b and c are numbers: 2 (x + 3) goes to 2 x + 6. The formula (-a) (b - c), where -a is a negative number, is rewritten to a (c - b).

The distributive law of products and powers is used for adjacent terms of the product: x^a x^b goes to x^(a+b) where a is a number, or an implicit 1 (as in x), or the implicit one-half of sqrt(x), and similarly for b. The result is written using `sqrt' or `1/sqrt' if the sum of the powers is 1/2 or -1/2, respectively. If the sum of the powers is zero, the product is simplified to 1 or to `idn(1)' if matrix mode is enabled.

The product of a negative power times anything but another negative power is changed to use division: x^(-2) y goes to y / x^2 unless matrix mode is in effect and neither x nor y are scalar (in which case it is considered unsafe to rearrange the order of the terms).

Finally, a (b/c) is rewritten to (a b)/c, and also (a/b) c is changed to (a c)/b unless in matrix mode.

Simplifications for quotients are analogous to those for products. The quotient 0 / x is simplified to 0, with the same exceptions that were noted for 0 x. Likewise, x / 1 and x / (-1) are simplified to x and -x, respectively.

The quotient x / 0 is left unsimplified or changed to an infinite quantity, as directed by the current infinite mode. See section 8.4.4 Infinite Mode.

The expression a / b^(-c) is changed to a b^c, where -c is any negative-looking power. Also, 1 / b^c is changed to b^(-c) for any power c.

Also, (-a) / b and a / (-b) go to -(a/b); (a/b) / c goes to a / (b c); and a / (b/c) goes to (a c) / b unless matrix mode prevents this rearrangement. Similarly, a / (b:c) is simplified to (c:b) a for any fraction b:c.

The distributive law is applied to (a + b) / c only if c and at least one of a and b are numbers. Quotients of powers and square roots are distributed just as described for multiplication.

Quotients of products cancel only in the leading terms of the numerator and denominator. In other words, a x b / a y b is cancelled to x b / y b but not to x / y. Once again this is because full cancellation can be slow; use a s to cancel all terms of the quotient.

Quotients of negative-looking values are simplified according to (-a) / (-b) to a / b, (-a) / (b - c) to a / (c - b), and (a - b) / (-c) to (b - a) / c.

The formula x^0 is simplified to 1, or to `idn(1)' in matrix mode. The formula 0^x is simplified to 0 unless x is a negative number or complex number, in which case the result is an infinity or an unsimplified formula according to the current infinite mode. Note that 0^0 is an indeterminate form, as evidenced by the fact that the simplifications for x^0 and 0^x conflict when x=0.

Powers of products or quotients (a b)^c, (a/b)^c are distributed to a^c b^c, a^c / b^c only if c is an integer, or if either a or b are nonnegative real numbers. Powers of powers (a^b)^c are simplified to a^(b c) only when c is an integer and b c also evaluates to an integer. Without these restrictions these simplifications would not be safe because of problems with principal values. (In other words, ((-3)^1:2)^2 is safe to simplify, but ((-3)^2)^1:2 is not.) See section 8.6 Declarations, for ways to inform Calc that your variables satisfy these requirements.

As a special case of this rule, sqrt(x)^n is simplified to x^(n/2) only for even integers n.

If a is known to be real, b is an even integer, and c is a half- or quarter-integer, then (a^b)^c is simplified to abs(a^(b c)).

Also, (-a)^b is simplified to a^b if b is an even integer, or to -(a^b) if b is an odd integer, for any negative-looking expression -a.

Square roots sqrt(x) generally act like one-half powers x^1:2 for the purposes of the above-listed simplifications.

Also, note that 1 / x^1:2 is changed to x^(-1:2), but 1 / sqrt(x) is left alone.

Generic identity matrices (see section 8.4.6 Matrix and Scalar Modes) are simplified by the following rules: idn(a) + b to a + b if b is provably scalar, or expanded out if b is a matrix; idn(a) + idn(b) to idn(a + b); -idn(a) to idn(-a); a idn(b) to idn(a b) if a is provably scalar, or to a b if a is provably non-scalar; idn(a) idn(b) to idn(a b); analogous simplifications for quotients involving idn; and idn(a)^n to idn(a^n) where n is an integer.

The floor function and other integer truncation functions vanish if the argument is provably integer-valued, so that floor(round(x)) simplifies to round(x). Also, combinations of float, floor and its friends, and ffloor and its friends, are simplified in appropriate ways. See section 9.2 Integer Truncation.

The expression abs(-x) changes to abs(x). The expression abs(abs(x)) changes to abs(x); in fact, abs(x) changes to x or -x if x is provably nonnegative or nonpositive (see section 8.6 Declarations).

While most functions do not recognize the variable i as an imaginary number, the arg function does handle the two cases arg(i) and arg(-i) just for convenience.

The expression conj(conj(x)) simplifies to x. Various other expressions involving conj, re, and im are simplified, especially if some of the arguments are provably real or involve the constant i. For example, conj(a + b i) is changed to conj(a) - conj(b) i, or to a - b i if a and b are known to be real.

Functions like sin and arctan generally don't have any default simplifications beyond simply evaluating the functions for suitable numeric arguments and infinity. The a s command described in the next section does provide some simplifications for these functions, though.

One important simplification that does occur is that ln(e) is simplified to 1, and ln(e^x) is simplified to x for any x. This occurs even if you have stored a different value in the Calc variable `e'; but this would be a bad idea in any case if you were also using natural logarithms!

Among the logical functions, !(a <= b) changes to a > b and so on. Equations and inequalities where both sides are either negative-looking or zero are simplified by negating both sides and reversing the inequality. While it might seem reasonable to simplify !!x to x, this would not be valid in general because !!2 is 1, not 2.

Most other Calc functions have few if any default simplifications defined, aside of course from evaluation when the arguments are suitable numbers.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.3.2 Algebraic Simplifications

The a s command makes simplifications that may be too slow to do all the time, or that may not be desirable all of the time. If you find these simplifications are worthwhile, you can type m A to have Calc apply them automatically.

This section describes all simplifications that are performed by the a s command. Note that these occur in addition to the default simplifications; even if the default simplifications have been turned off by an m O command, a s will turn them back on temporarily while it simplifies the formula.

There is a variable, AlgSimpRules, in which you can put rewrites to be applied by a s. Its use is analogous to EvalRules, but without the special restrictions. Basically, the simplifier does `a r AlgSimpRules' with an infinite repeat count on the whole expression being simplified, then it traverses the expression applying the built-in rules described below. If the result is different from the original expression, the process repeats with the default simplifications (including EvalRules), then AlgSimpRules, then the built-in simplifications, and so on.

Sums are simplified in two ways. Constant terms are commuted to the end of the sum, so that a + 2 + b changes to a + b + 2. The only exception is that a constant will not be commuted away from the first position of a difference, i.e., 2 - x is not commuted to -x + 2.

Also, terms of sums are combined by the distributive law, as in x + y + 2 x to y + 3 x. This always occurs for adjacent terms, but a s compares all pairs of terms including non-adjacent ones.

Products are sorted into a canonical order using the commutative law. For example, b c a is commuted to a b c. This allows easier comparison of products; for example, the default simplifications will not change x y + y x to 2 x y, but a s will; it first rewrites the sum to x y + x y, and then the default simplifications are able to recognize a sum of identical terms.

The canonical ordering used to sort terms of products has the property that real-valued numbers, interval forms and infinities come first, and are sorted into increasing order. The V S command uses the same ordering when sorting a vector.

Sorting of terms of products is inhibited when matrix mode is turned on; in this case, Calc will never exchange the order of two terms unless it knows at least one of the terms is a scalar.

Products of powers are distributed by comparing all pairs of terms, using the same method that the default simplifications use for adjacent terms of products.

Even though sums are not sorted, the commutative law is still taken into account when terms of a product are being compared. Thus (x + y) (y + x) will be simplified to (x + y)^2. A subtle point is that (x - y) (y - x) will not be simplified to -(x - y)^2; Calc does not notice that one term can be written as a constant times the other, even if that constant is -1.

A fraction times any expression, (a:b) x, is changed to a quotient involving integers: a x / b. This is not done for floating-point numbers like 0.5, however. This is one reason why you may find it convenient to turn Fraction mode on while doing algebra; see section 8.4.3 Fraction Mode.

Quotients are simplified by comparing all terms in the numerator with all terms in the denominator for possible cancellation using the distributive law. For example, a x^2 b / c x^3 d will cancel x^2 from both sides to get a b / c x d. (The terms in the denominator will then be rearranged to c d x as described above.) If there is any common integer or fractional factor in the numerator and denominator, it is cancelled out; for example, (4 x + 6) / 8 x simplifies to (2 x + 3) / 4 x.

Non-constant common factors are not found even by a s. To cancel the factor a in (a x + a) / a^2 you could first use j M on the product a x to Merge the numerator to a (1+x), which can then be simplified successfully.

Integer powers of the variable i are simplified according to the identity i^2 = -1. If you store a new value other than the complex number (0,1) in i, this simplification will no longer occur. This is done by a s instead of by default in case someone (unwisely) uses the name i for a variable unrelated to complex numbers; it would be unfortunate if Calc quietly and automatically changed this formula for reasons the user might not have been thinking of.

Square roots of integer or rational arguments are simplified in several ways. (Note that these will be left unevaluated only in Symbolic mode.) First, square integer or rational factors are pulled out so that sqrt(8) is rewritten as 2 sqrt(2). Conceptually speaking this implies factoring the argument into primes and moving pairs of primes out of the square root, but for reasons of efficiency Calc only looks for primes up to 29.

Square roots in the denominator of a quotient are moved to the numerator: 1 / sqrt(3) changes to sqrt(3) / 3. The same effect occurs for the square root of a fraction: sqrt(2:3) changes to sqrt(6) / 3.

The % (modulo) operator is simplified in several ways when the modulus M is a positive real number. First, if the argument is of the form x + n for some real number n, then n is itself reduced modulo M. For example, `(x - 23) % 10' is simplified to `(x + 7) % 10'.

If the argument is multiplied by a constant, and this constant has a common integer divisor with the modulus, then this factor is cancelled out. For example, `12 x % 15' is changed to `3 (4 x % 5)' by factoring out 3. Also, `(12 x + 1) % 15' is changed to `3 ((4 x + 1:3) % 5)'. While these forms may not seem "simpler," they allow Calc to discover useful information about modulo forms in the presence of declarations.

If the modulus is 1, then Calc can use int declarations to evaluate the expression. For example, the idiom `x % 2' is often used to check whether a number is odd or even. As described above, `2 n % 2' and `(2 n + 1) % 2' are simplified to `2 (n % 1)' and `2 ((n + 1:2) % 1)', respectively; Calc can simplify these to 0 and 1 (respectively) if n has been declared to be an integer.

Trigonometric functions are simplified in several ways. First, sin(arcsin(x)) is simplified to x, and similarly for cos and tan. If the argument to sin is negative-looking, it is simplified to -sin(x), and similarly for cos and tan. Finally, certain special values of the argument are recognized; see section 10.2 Trigonometric/Hyperbolic Functions.

Trigonometric functions of inverses of different trigonometric functions can also be simplified, as in sin(arccos(x)) to sqrt(1 - x^2).

Hyperbolic functions of their inverses and of negative-looking arguments are also handled, as are exponentials of inverse hyperbolic functions.

No simplifications for inverse trigonometric and hyperbolic functions are known, except for negative arguments of arcsin, arctan, arcsinh, and arctanh. Note that arcsin(sin(x)) can not safely change to x, since this only correct within an integer multiple of 2 pi radians or 360 degrees. However, arcsinh(sinh(x)) is simplified to x if x is known to be real.

Several simplifications that apply to logarithms and exponentials are that exp(ln(x)), e^ln(x), and 10^log10(x) all reduce to x. Also, ln(exp(x)), etc., can reduce to x if x is provably real. The form exp(x)^y is simplified to exp(x y). If x is a suitable multiple of pi i (as described above for the trigonometric functions), then exp(x) or e^x will be expanded. Finally, ln(x) is simplified to a form involving pi and i where x is provably negative, positive imaginary, or negative imaginary.

The error functions erf and erfc are simplified when their arguments are negative-looking or are calls to the conj function.

Equations and inequalities are simplified by cancelling factors of products, quotients, or sums on both sides. Inequalities change sign if a negative multiplicative factor is cancelled. Non-constant multiplicative factors as in a b = a c are cancelled from equations only if they are provably nonzero (generally because they were declared so; see section 8.6 Declarations). Factors are cancelled from inequalities only if they are nonzero and their sign is known.

Simplification also replaces an equation or inequality with 1 or 0 ("true" or "false") if it can through the use of declarations. If x is declared to be an integer greater than 5, then x < 3, x = 3, and x = 7.5 are all simplified to 0, but x > 3 is simplified to 1. By a similar analysis, abs(x) >= 0 is simplified to 1, as is x^2 >= 0 if x is known to be real.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.3.3 "Unsafe" Simplifications

The a e (calc-simplify-extended) [esimplify] command is like a s except that it applies some additional simplifications which are not "safe" in all cases. Use this only if you know the values in your formula lie in the restricted ranges for which these simplifications are valid. The symbolic integrator uses a e; one effect of this is that the integrator's results must be used with caution. Where an integral table will often attach conditions like "for positive a only," Calc (like most other symbolic integration programs) will simply produce an unqualified result.

Because a e's simplifications are unsafe, it is sometimes better to type C-u -3 a v, which does extended simplification only on the top level of the formula without affecting the sub-formulas. In fact, C-u -3 j v allows you to target extended simplification to any specific part of a formula.

The variable ExtSimpRules contains rewrites to be applied by the a e command. These are applied in addition to EvalRules and AlgSimpRules. (The a r AlgSimpRules step described above is simply followed by an a r ExtSimpRules step.)

Following is a complete list of "unsafe" simplifications performed by a e.

Inverse trigonometric or hyperbolic functions, called with their corresponding non-inverse functions as arguments, are simplified by a e. For example, arcsin(sin(x)) changes to x. Also, arcsin(cos(x)) and arccos(sin(x)) both change to pi/2 - x. These simplifications are unsafe because they are valid only for values of x in a certain range; outside that range, values are folded down to the 360-degree range that the inverse trigonometric functions always produce.

Powers of powers (x^a)^b are simplified to x^(a b) for all a and b. These results will be valid only in a restricted range of x; for example, in (x^2)^1:2 the powers cancel to get x, which is valid for positive values of x but not for negative or complex values.

Similarly, sqrt(x^a) and sqrt(x)^a are both simplified (possibly unsafely) to x^(a/2).

Forms like sqrt(1 - sin(x)^2) are simplified to, e.g., cos(x). Calc has identities of this sort for sin, cos, tan, sinh, and cosh.

Arguments of square roots are partially factored to look for squared terms that can be extracted. For example, sqrt(a^2 b^3 + a^3 b^2) simplifies to a b sqrt(a+b).

The simplifications of ln(exp(x)), ln(e^x), and log10(10^x) to x are also unsafe because of problems with principal values (although these simplifications are safe if x is known to be real).

Common factors are cancelled from products on both sides of an equation, even if those factors may be zero: a x / b x to a / b. Such factors are never cancelled from inequalities: Even a e is not bold enough to reduce a x < b x to a < b (or a > b, depending on whether you believe x is positive or negative). The a M / command can be used to divide a factor out of both sides of an inequality.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.3.4 Simplification of Units

The simplifications described in this section are applied by the u s (calc-simplify-units) command. These are in addition to the regular a s (but not a e) simplifications described earlier. See section 13.1 Basic Operations on Units.

The variable UnitSimpRules contains rewrites to be applied by the u s command. These are applied in addition to EvalRules and AlgSimpRules.

Scalar mode is automatically put into effect when simplifying units. See section 8.4.6 Matrix and Scalar Modes.

Sums a + b involving units are simplified by extracting the units of a as if by the u x command (call the result u_a), then simplifying the expression b / u_a using u b and u s. If the result has units then the sum is inconsistent and is left alone. Otherwise, it is rewritten in terms of the units u_a.

If units auto-ranging mode is enabled, products or quotients in which the first argument is a number which is out of range for the leading unit are modified accordingly.

When cancelling and combining units in products and quotients, Calc accounts for unit names that differ only in the prefix letter. For example, `2 km m' is simplified to `2000 m^2'. However, compatible but different units like ft and in are not combined in this way.

Quotients a / b are simplified in three additional ways. First, if b is a number or a product beginning with a number, Calc computes the reciprocal of this number and moves it to the numerator.

Second, for each pair of unit names from the numerator and denominator of a quotient, if the units are compatible (e.g., they are both units of area) then they are replaced by the ratio between those units. For example, in `3 s in N / kg cm' the units `in / cm' will be replaced by 2.54.

Third, if the units in the quotient exactly cancel out, so that a u b command on the quotient would produce a dimensionless number for an answer, then the quotient simplifies to that number.

For powers and square roots, the "unsafe" simplifications (a b)^c to a^c b^c, (a/b)^c to a^c / b^c, and (a^b)^c to a^(b c) are done if the powers are real numbers. (These are safe in the context of units because all numbers involved can reasonably be assumed to be real.)

Also, if a unit name is raised to a fractional power, and the base units in that unit name all occur to powers which are a multiple of the denominator of the power, then the unit name is expanded out into its base units, which can then be simplified according to the previous paragraph. For example, `acre^1.5' is simplified by noting that 1.5 = 3:2, that `acre' is defined in terms of `m^2', and that the 2 in the power of m is a multiple of 2 in 3:2. Thus, acre^1.5 is replaced by approximately (4046 m^2)^1.5, which is then changed to 4046^1.5 (m^2)^1.5, then to 257440 m^3.

The functions float, frac, clean, abs, as well as floor and the other integer truncation functions, applied to unit names or products or quotients involving units, are simplified. For example, `round(1.6 in)' is changed to `round(1.6) round(in)'; the lefthand term evaluates to 2, and the righthand term simplifies to in.

The functions sin, cos, and tan with arguments that have angular units like rad or arcmin are simplified by converting to base units (radians), then evaluating with the angular mode temporarily set to radians.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.4 Polynomials

A polynomial is a sum of terms which are coefficients times various powers of a "base" variable. For example, 2 x^2 + 3 x - 4 is a polynomial in x. Some formulas can be considered polynomials in several different variables: 1 + 2 x + 3 y + 4 x y^2 is a polynomial in both x and y. Polynomial coefficients are often numbers, but they may in general be any formulas not involving the base variable.

The a f (calc-factor) [factor] command factors a polynomial into a product of terms. For example, the polynomial x^3 + 2 x^2 + x is factored into `x*(x+1)^2'. As another example, a c + b d + b c + a d is factored into the product (a + b) (c + d).

Calc currently has three algorithms for factoring. Formulas which are linear in several variables, such as the second example above, are merged according to the distributive law. Formulas which are polynomials in a single variable, with constant integer or fractional coefficients, are factored into irreducible linear and/or quadratic terms. The first example above factors into three linear terms (x, x+1, and x+1 again). Finally, formulas which do not fit the above criteria are handled by the algebraic rewrite mechanism.

Calc's polynomial factorization algorithm works by using the general root-finding command (a P) to solve for the roots of the polynomial. It then looks for roots which are rational numbers or complex-conjugate pairs, and converts these into linear and quadratic terms, respectively. Because it uses floating-point arithmetic, it may be unable to find terms that involve large integers (whose number of digits approaches the current precision). Also, irreducible factors of degree higher than quadratic are not found, and polynomials in more than one variable are not treated. (A more robust factorization algorithm may be included in a future version of Calc.)

The rewrite-based factorization method uses rules stored in the variable FactorRules. See section 12.11 Rewrite Rules, for a discussion of the operation of rewrite rules. The default FactorRules are able to factor quadratic forms symbolically into two linear terms, (a x + b) (c x + d). You can edit these rules to include other cases if you wish. To use the rules, Calc builds the formula `thecoefs(x, [a, b, c, ...])' where x is the polynomial base variable and a, b, etc., are polynomial coefficients (which may be numbers or formulas). The constant term is written first, i.e., in the a position. When the rules complete, they should have changed the formula into the form `thefactors(x, [f1, f2, f3, ...])' where each fi should be a factored term, e.g., `x - ai'. Calc then multiplies these terms together to get the complete factored form of the polynomial. If the rules do not change the thecoefs call to a thefactors call, a f leaves the polynomial alone on the assumption that it is unfactorable. (Note that the function names thecoefs and thefactors are used only as placeholders; there are no actual Calc functions by those names.)

The H a f [factors] command also factors a polynomial, but it returns a list of factors instead of an expression which is the product of the factors. Each factor is represented by a sub-vector of the factor, and the power with which it appears. For example, x^5 + x^4 - 33 x^3 + 63 x^2 factors to (x + 7) x^2 (x - 3)^2 in a f, or to [ [x, 2], [x+7, 1], [x-3, 2] ] in H a f. If there is an overall numeric factor, it always comes first in the list. The functions factor and factors allow a second argument when written in algebraic form; `factor(x,v)' factors x with respect to the specific variable v. The default is to factor with respect to all the variables that appear in x.

The a c (calc-collect) [collect] command rearranges a formula as a polynomial in a given variable, ordered in decreasing powers of that variable. For example, given 1 + 2 x + 3 y + 4 x y^2 on the stack, a c x would produce (2 + 4 y^2) x + (1 + 3 y), and a c y would produce (4 x) y^2 + 3 y + (1 + 2 x). The polynomial will be expanded out using the distributive law as necessary: Collecting x in (x - 1)^3 produces x^3 - 3 x^2 + 3 x - 1. Terms not involving x will not be expanded.

The "variable" you specify at the prompt can actually be any expression: a c ln(x+1) will collect together all terms multiplied by `ln(x+1)' or integer powers thereof. If `x' also appears in the formula in a context other than `ln(x+1)', a c will treat those occurrences as unrelated to `ln(x+1)', i.e., as constants.

The a x (calc-expand) [expand] command expands an expression by applying the distributive law everywhere. It applies to products, quotients, and powers involving sums. By default, it fully distributes all parts of the expression. With a numeric prefix argument, the distributive law is applied only the specified number of times, then the partially expanded expression is left on the stack.

The a x and j D commands are somewhat redundant. Use a x if you want to expand all products of sums in your formula. Use j D if you want to expand a particular specified term of the formula. There is an exactly analogous correspondence between a f and j M. (The j D and j M commands also know many other kinds of expansions, such as `exp(a + b) = exp(a) exp(b)', which a x and a f do not do.)

Calc's automatic simplifications will sometimes reverse a partial expansion. For example, the first step in expanding (x+1)^3 is to write (x+1) (x+1)^2. If a x stops there and tries to put this formula onto the stack, though, Calc will automatically simplify it back to (x+1)^3 form. The solution is to turn simplification off first (see section 8.5 Simplification Modes), or to run a x without a numeric prefix argument so that it expands all the way in one step.

The a a (calc-apart) [apart] command expands a rational function by partial fractions. A rational function is the quotient of two polynomials; apart pulls this apart into a sum of rational functions with simple denominators. In algebraic notation, the apart function allows a second argument that specifies which variable to use as the "base"; by default, Calc chooses the base variable automatically.

The a n (calc-normalize-rat) [nrat] command attempts to arrange a formula into a quotient of two polynomials. For example, given 1 + (a + b/c) / d, the result would be (b + a c + c d) / c d. The quotient is reduced, so that a n will simplify (x^2 + 2x + 1) / (x^2 - 1) by dividing out the common factor x + 1, yielding (x + 1) / (x - 1).

The a \ (calc-poly-div) [pdiv] command divides two polynomials u and v, yielding a new polynomial q. If several variables occur in the inputs, the inputs are considered multivariate polynomials. (Calc divides by the variable with the largest power in u first, or, in the case of equal powers, chooses the variables in alphabetical order.) For example, dividing x^2 + 3 x + 2 by x + 2 yields x + 1. The remainder from the division, if any, is reported at the bottom of the screen and is also placed in the Trail along with the quotient.

Using pdiv in algebraic notation, you can specify the particular variable to be used as the base: `pdiv(a,b,x)'. If pdiv is given only two arguments (as is always the case with the a \ command), then it does a multivariate division as outlined above.

The a % (calc-poly-rem) [prem] command divides two polynomials and keeps the remainder r. The quotient q is discarded. For any formulas a and b, the results of a \ and a % satisfy a = q b + r. (This is analogous to plain \ and %, which compute the integer quotient and remainder from dividing two numbers.)

The a / (calc-poly-div-rem) [pdivrem] command divides two polynomials and reports both the quotient and the remainder as a vector [q, r]. The H a / [pdivide] command divides two polynomials and constructs the formula q + r/b on the stack. (Naturally if the remainder is zero, this will immediately simplify to q.)

The a g (calc-poly-gcd) [pgcd] command computes the greatest common divisor of two polynomials. (The GCD actually is unique only to within a constant multiplier; Calc attempts to choose a GCD which will be unsurprising.) For example, the a n command uses a g to take the GCD of the numerator and denominator of a quotient, then divides each by the result using a \. (The definition of GCD ensures that this division can take place without leaving a remainder.)

While the polynomials used in operations like a / and a g often have integer coefficients, this is not required. Calc can also deal with polynomials over the rationals or floating-point reals. Polynomials with modulo-form coefficients are also useful in many applications; if you enter `(x^2 + 3 x - 1) mod 5', Calc automatically transforms this into a polynomial over the field of integers mod 5: `(1 mod 5) x^2 + (3 mod 5) x + (4 mod 5)'.

Congratulations and thanks go to Ove Ewerlid (ewerlid@mizar.DoCS.UU.SE), who contributed many of the polynomial routines used in the above commands.

See section 12.6.3 Decomposing Polynomials, for several useful functions for extracting the individual coefficients of a polynomial.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.5 Calculus

The following calculus commands do not automatically simplify their inputs or outputs using calc-simplify. You may find it helps to do this by hand by typing a s or a e. It may also help to use a x and/or a c to arrange a result in the most readable way.

12.5.1 Differentiation  
12.5.2 Integration  
12.5.3 Customizing the Integrator  
12.5.4 Numerical Integration  
12.5.5 Taylor Series  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.5.1 Differentiation

The a d (calc-derivative) [deriv] command computes the derivative of the expression on the top of the stack with respect to some variable, which it will prompt you to enter. Normally, variables in the formula other than the specified differentiation variable are considered constant, i.e., `deriv(y,x)' is reduced to zero. With the Hyperbolic flag, the tderiv (total derivative) operation is used instead, in which derivatives of variables are not reduced to zero unless those variables are known to be "constant," i.e., independent of any other variables. (The built-in special variables like pi are considered constant, as are variables that have been declared const; see section 8.6 Declarations.)

With a numeric prefix argument n, this command computes the nth derivative.

When working with trigonometric functions, it is best to switch to radians mode first (with m r). The derivative of `sin(x)' in degrees is `(pi/180) cos(x)', probably not the expected answer!

If you use the deriv function directly in an algebraic formula, you can write `deriv(f,x,x0)' which represents the derivative of f with respect to x, evaluated at the point x=x0.

If the formula being differentiated contains functions which Calc does not know, the derivatives of those functions are produced by adding primes (apostrophe characters). For example, `deriv(f(2x), x)' produces `2 f'(2 x)', where the function f' represents the derivative of f.

For functions you have defined with the Z F command, Calc expands the functions according to their defining formulas unless you have also defined f' suitably. For example, suppose we define `sinc(x) = sin(x)/x' using Z F. If we then differentiate the formula `sinc(2 x)', the formula will be expanded to `sin(2 x) / (2 x)' and differentiated. However, if we also define `sinc'(x) = dsinc(x)', say, then Calc will write the result as `2 dsinc(2 x)'. See section 19.4 Programming with Formulas.

For multi-argument functions `f(x,y,z)', the derivative with respect to the first argument is written `f'(x,y,z)'; derivatives with respect to the other arguments are `f'2(x,y,z)' and `f'3(x,y,z)'. Various higher-order derivatives can be formed in the obvious way, e.g., `f'''(x) (the second derivative of f) or `f'''2'3(x,y,z) (f differentiated with respect to each argument once).

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.5.2 Integration

The a i (calc-integral) [integ] command computes the indefinite integral of the expression on the top of the stack with respect to a variable. The integrator is not guaranteed to work for all integrable functions, but it is able to integrate several large classes of formulas. In particular, any polynomial or rational function (a polynomial divided by a polynomial) is acceptable. (Rational functions don't have to be in explicit quotient form, however; x/(1+x^-2) is not strictly a quotient of polynomials, but it is equivalent to x^3/(x^2+1), which is.) Also, square roots of terms involving x and x^2 may appear in rational functions being integrated. Finally, rational functions involving trigonometric or hyperbolic functions can be integrated.

If you use the integ function directly in an algebraic formula, you can also write `integ(f,x,v)' which expresses the resulting indefinite integral in terms of variable v instead of x. With four arguments, `integ(f(x),x,a,b)' represents a definite integral from a to b.

Please note that the current implementation of Calc's integrator sometimes produces results that are significantly more complex than they need to be. For example, the integral Calc finds for 1/(x+sqrt(x^2+1)) is several times more complicated than the answer Mathematica returns for the same input, although the two forms are numerically equivalent. Also, any indefinite integral should be considered to have an arbitrary constant of integration added to it, although Calc does not write an explicit constant of integration in its result. For example, Calc's solution for 1/(1+tan(x)) differs from the solution given in the CRC Math Tables by a constant factor of pi i / 2, due to a different choice of constant of integration.

The Calculator remembers all the integrals it has done. If conditions change in a way that would invalidate the old integrals, say, a switch from degrees to radians mode, then they will be thrown out. If you suspect this is not happening when it should, use the calc-flush-caches command; see section 5.11.3 Caches.

Calc normally will pursue integration by substitution or integration by parts up to 3 nested times before abandoning an approach as fruitless. If the integrator is taking too long, you can lower this limit by storing a number (like 2) in the variable IntegLimit. (The s I command is a convenient way to edit IntegLimit.) If this variable has no stored value or does not contain a nonnegative integer, a limit of 3 is used. The lower this limit is, the greater the chance that Calc will be unable to integrate a function it could otherwise handle. Raising this limit allows the Calculator to solve more integrals, though the time it takes may grow exponentially. You can monitor the integrator's actions by creating an Emacs buffer called *Trace*. If such a buffer exists, the a i command will write a log of its actions there.

If you want to manipulate integrals in a purely symbolic way, you can set the integration nesting limit to 0 to prevent all but fast table-lookup solutions of integrals. You might then wish to define rewrite rules for integration by parts, various kinds of substitutions, and so on. See section 12.11 Rewrite Rules.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.5.3 Customizing the Integrator

Calc has two built-in rewrite rules called IntegRules and IntegAfterRules which you can edit to define new integration methods. See section 12.11 Rewrite Rules. At each step of the integration process, Calc wraps the current integrand in a call to the fictitious function `integtry(expr,var)', where expr is the integrand and var is the integration variable. If your rules rewrite this to be a plain formula (not a call to integtry), then Calc will use this formula as the integral of expr. For example, the rule `integtry(mysin(x),x) := -mycos(x)' would define a rule to integrate a function mysin that acts like the sine function. Then, putting `4 mysin(2y+1)' on the stack and typing a i y will produce the integral `-2 mycos(2y+1)'. Note that Calc has automatically made various transformations on the integral to allow it to use your rule; integral tables generally give rules for `mysin(a x + b)', but you don't need to use this much generality in your IntegRules.

As a more serious example, the expression `exp(x)/x' cannot be integrated in terms of the standard functions, so the "exponential integral" function Ei(x) was invented to describe it. We can get Calc to do this integral in terms of a made-up Ei function by adding the rule `[integtry(exp(x)/x, x) := Ei(x)]' to IntegRules. Now entering `exp(2x)/x' on the stack and typing a i x yields `Ei(2 x)'. This new rule will work with Calc's various built-in integration methods (such as integration by substitution) to solve a variety of other problems involving Ei: For example, now Calc will also be able to integrate `exp(exp(x))' and `ln(ln(x))' (to get `Ei(exp(x))' and `x ln(ln(x)) - Ei(ln(x))', respectively).

Your rule may do further integration by calling integ. For example, `integtry(twice(u),x) := twice(integ(u))' allows Calc to integrate `twice(sin(x))' to get `twice(-cos(x))'. Note that integ was called with only one argument. This notation is allowed only within IntegRules; it means "integrate this with respect to the same integration variable." If Calc is unable to integrate u, the integration that invoked IntegRules also fails. Thus integrating `twice(f(x))' fails, returning the unevaluated integral `integ(twice(f(x)), x)'. It is still legal to call integ with two or more arguments, however; in this case, if u is not integrable, twice itself will still be integrated: If the above rule is changed to `... := twice(integ(u,x))', then integrating `twice(f(x))' will yield `twice(integ(f(x),x))'.

If a rule instead produces the formula `integsubst(sexpr, svar)', either replacing the top-level integtry call or nested anywhere inside the expression, then Calc will apply the substitution `u = sexpr(svar)' to try to integrate the original expr. For example, the rule `sqrt(a) := integsubst(sqrt(x),x)' says that if Calc ever finds a square root in the integrand, it should attempt the substitution `u = sqrt(x)'. (This particular rule is unnecessary because Calc always tries "obvious" substitutions where sexpr actually appears in the integrand.) The variable svar may be the same as the var that appeared in the call to integtry, but it need not be.

When integrating according to an integsubst, Calc uses the equation solver to find the inverse of sexpr (if the integrand refers to var anywhere except in subexpressions that exactly match sexpr). It uses the differentiator to find the derivative of sexpr and/or its inverse (it has two methods that use one derivative or the other). You can also specify these items by adding extra arguments to the integsubst your rules construct; the general form is `integsubst(sexpr, svar, sinv, sprime)', where sinv is the inverse of sexpr (still written as a function of svar), and sprime is the derivative of sexpr with respect to svar. If you don't specify these things, and Calc is not able to work them out on its own with the information it knows, then your substitution rule will work only in very specific, simple cases.

Calc applies IntegRules as if by C-u 1 a r IntegRules; in other words, Calc stops rewriting as soon as any rule in your rule set succeeds. (If it weren't for this, the `integsubst(sqrt(x),x)' example above would keep on adding layers of integsubst calls forever!)

Another set of rules, stored in IntegSimpRules, are applied every time the integrator uses a s to simplify an intermediate result. For example, putting the rule `twice(x) := 2 x' into IntegSimpRules would tell Calc to convert the twice function into a form it knows whenever integration is attempted.

One more way to influence the integrator is to define a function with the Z F command (see section 19.4 Programming with Formulas). Calc's integrator automatically expands such functions according to their defining formulas, even if you originally asked for the function to be left unevaluated for symbolic arguments. (Certain other Calc systems, such as the differentiator and the equation solver, also do this.)

Sometimes Calc is able to find a solution to your integral, but it expresses the result in a way that is unnecessarily complicated. If this happens, you can either use integsubst as described above to try to hint at a more direct path to the desired result, or you can use IntegAfterRules. This is an extra rule set that runs after the main integrator returns its result; basically, Calc does an a r IntegAfterRules on the result before showing it to you. (It also does an a s, without IntegSimpRules, after that to further simplify the result.) For example, Calc's integrator sometimes produces expressions of the form `ln(1+x) - ln(1-x)'; the default IntegAfterRules rewrite this into the more readable form `2 arctanh(x)'. Note that, unlike IntegRules, IntegSimpRules and IntegAfterRules are applied any number of times until no further changes are possible. Rewriting by IntegAfterRules occurs only after the main integrator has finished, not at every step as for IntegRules and IntegSimpRules.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.5.4 Numerical Integration

If you want a purely numerical answer to an integration problem, you can use the a I (calc-num-integral) [ninteg] command. This command prompts for an integration variable, a lower limit, and an upper limit. Except for the integration variable, all other variables that appear in the integrand formula must have stored values. (A stored value, if any, for the integration variable itself is ignored.)

Numerical integration works by evaluating your formula at many points in the specified interval. Calc uses an "open Romberg" method; this means that it does not evaluate the formula actually at the endpoints (so that it is safe to integrate `sin(x)/x' from zero, for example). Also, the Romberg method works especially well when the function being integrated is fairly smooth. If the function is not smooth, Calc will have to evaluate it at quite a few points before it can accurately determine the value of the integral.

Integration is much faster when the current precision is small. It is best to set the precision to the smallest acceptable number of digits before you use a I. If Calc appears to be taking too long, press C-g to halt it and try a lower precision. If Calc still appears to need hundreds of evaluations, check to make sure your function is well-behaved in the specified interval.

It is possible for the lower integration limit to be `-inf' (minus infinity). Likewise, the upper limit may be plus infinity. Calc internally transforms the integral into an equivalent one with finite limits. However, integration to or across singularities is not supported: The integral of `1/sqrt(x)' from 0 to 1 exists (it can be found by Calc's symbolic integrator, for example), but a I will fail because the integrand goes to infinity at one of the endpoints.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.5.5 Taylor Series

The a t (calc-taylor) [taylor] command computes a power series expansion or Taylor series of a function. You specify the variable and the desired number of terms. You may give an expression of the form `var = a' or `var - a' instead of just a variable to produce a Taylor expansion about the point a. You may specify the number of terms with a numeric prefix argument; otherwise the command will prompt you for the number of terms. Note that many series expansions have coefficients of zero for some terms, so you may appear to get fewer terms than you asked for.

If the a i command is unable to find a symbolic integral for a function, you can get an approximation by integrating the function's Taylor series.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.6 Solving Equations

The a S (calc-solve-for) [solve] command rearranges an equation to solve for a specific variable. An equation is an expression of the form L = R. For example, the command a S x will rearrange y = 3x + 6 to the form, x = y/3 - 2. If the input is not an equation, it is treated like an equation of the form X = 0.

This command also works for inequalities, as in y < 3x + 6. Some inequalities cannot be solved where the analogous equation could be; for example, solving a < b c for b is impossible without knowing the sign of c. In this case, a S will produce the result b != a/c (using the not-equal-to operator) to signify that the direction of the inequality is now unknown. The inequality a <= b c is not even partially solved. See section 8.6 Declarations, for a way to tell Calc that the signs of the variables in a formula are in fact known.

Two useful commands for working with the result of a S are a . (see section 12.10 Logical Operations), which converts x = y/3 - 2 to y/3 - 2, and s l (see section 14.4 The Let Command) which evaluates another formula with x set equal to y/3 - 2.

12.6.1 Multiple Solutions  
12.6.2 Solving Systems of Equations  
12.6.3 Decomposing Polynomials  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.6.1 Multiple Solutions

Some equations have more than one solution. The Hyperbolic flag (H a S) [fsolve] tells the solver to report the fully general family of solutions. It will invent variables n1, n2, ..., which represent independent arbitrary integers, and s1, s2, ..., which represent independent arbitrary signs (either +1 or -1). If you don't use the Hyperbolic flag, Calc will use zero in place of all arbitrary integers, and plus one in place of all arbitrary signs. Note that variables like n1 and s1 are not given any special interpretation in Calc except by the equation solver itself. As usual, you can use the s l (calc-let) command to obtain solutions for various actual values of these variables.

For example, ' x^2 = y RET H a S x RET solves to get `x = s1 sqrt(y)', indicating that the two solutions to the equation are `sqrt(y)' and `-sqrt(y)'. Another way to think about it is that the square-root operation is really a two-valued function; since every Calc function must return a single result, sqrt chooses to return the positive result. Then H a S doctors this result using s1 to indicate the full set of possible values of the mathematical square-root.

There is a similar phenomenon going the other direction: Suppose we solve `sqrt(y) = x' for y. Calc squares both sides to get `y = x^2'. This is correct, except that it introduces some dubious solutions. Consider solving `sqrt(y) = -3': Calc will report y = 9 as a valid solution, which is true in the mathematical sense of square-root, but false (there is no solution) for the actual Calc positive-valued sqrt. This happens for both a S and H a S.

If you store a positive integer in the Calc variable GenCount, then Calc will generate formulas of the form `as(n)' for arbitrary signs, and `an(n)' for arbitrary integers, where n represents successive values taken by incrementing GenCount by one. While the normal arbitrary sign and integer symbols start over at s1 and n1 with each new Calc command, the GenCount approach will give each arbitrary value a name that is unique throughout the entire Calc session. Also, the arbitrary values are function calls instead of variables, which is advantageous in some cases. For example, you can make a rewrite rule that recognizes all arbitrary signs using a pattern like `as(n)'. The s l command only works on variables, but you can use the a b (calc-substitute) command to substitute actual values for function calls like `as(3)'.

The s G (calc-edit-GenCount) command is a convenient way to create or edit this variable. Press M-# M-# to finish.

If you have not stored a value in GenCount, or if the value in that variable is not a positive integer, the regular s1/n1 notation is used.

With the Inverse flag, I a S [finv] treats the expression on top of the stack as a function of the specified variable and solves to find the inverse function, written in terms of the same variable. For example, I a S x inverts 2x + 6 to x/2 - 3. You can use both Inverse and Hyperbolic [ffinv] to obtain a fully general inverse, as described above.

Some equations, specifically polynomials, have a known, finite number of solutions. The a P (calc-poly-roots) [roots] command uses H a S to solve an equation in general form, then, for all arbitrary-sign variables like s1, and all arbitrary-integer variables like n1 for which n1 only usefully varies over a finite range, it expands these variables out to all their possible values. The results are collected into a vector, which is returned. For example, `roots(x^4 = 1, x)' returns the four solutions `[1, -1, (0, 1), (0, -1)]'. Generally an nth degree polynomial will always have n roots on the complex plane. (If you have given a real declaration for the solution variable, then only the real-valued solutions, if any, will be reported; see section 8.6 Declarations.)

Note that because a P uses H a S, it is able to deliver symbolic solutions if the polynomial has symbolic coefficients. Also note that Calc's solver is not able to get exact symbolic solutions to all polynomials. Polynomials containing powers up to x^4 can always be solved exactly; polynomials of higher degree sometimes can be: x^6 + x^3 + 1 is converted to (x^3)^2 + (x^3) + 1, which can be solved for x^3 using the quadratic equation, and then for x by taking cube roots. But in many cases, like x^6 + x + 1, Calc does not know how to rewrite the polynomial into a form it can solve. The a P command can still deliver a list of numerical roots, however, provided that symbolic mode (m s) is not turned on. (If you work with symbolic mode on, recall that the N (calc-eval-num) key is a handy way to reevaluate the formula on the stack with symbolic mode temporarily off.) Naturally, a P can only provide numerical roots if the polynomial coefficients are all numbers (real or complex).

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.6.2 Solving Systems of Equations

You can also use the commands described above to solve systems of simultaneous equations. Just create a vector of equations, then specify a vector of variables for which to solve. (You can omit the surrounding brackets when entering the vector of variables at the prompt.)

For example, putting `[x + y = a, x - y = b]' on the stack and typing a S x,y RET produces the vector of solutions `[x = a - (a-b)/2, y = (a-b)/2]'. The result vector will have the same length as the variables vector, and the variables will be listed in the same order there. Note that the solutions are not always simplified as far as possible; the solution for x here could be improved by an application of the a n command.

Calc's algorithm works by trying to eliminate one variable at a time by solving one of the equations for that variable and then substituting into the other equations. Calc will try all the possibilities, but you can speed things up by noting that Calc first tries to eliminate the first variable with the first equation, then the second variable with the second equation, and so on. It also helps to put the simpler (e.g., more linear) equations toward the front of the list. Calc's algorithm will solve any system of linear equations, and also many kinds of nonlinear systems.

Normally there will be as many variables as equations. If you give fewer variables than equations (an "over-determined" system of equations), Calc will find a partial solution. For example, typing a S y RET with the above system of equations would produce `[y = a - x]'. There are now several ways to express this solution in terms of the original variables; Calc uses the first one that it finds. You can control the choice by adding variable specifiers of the form `elim(v)' to the variables list. This says that v should be eliminated from the equations; the variable will not appear at all in the solution. For example, typing a S y,elim(x) would yield `[y = a - (b+a)/2]'.

If the variables list contains only elim specifiers, Calc simply eliminates those variables from the equations and then returns the resulting set of equations. For example, a S elim(x) produces `[a - 2 y = b]'. Every variable eliminated will reduce the number of equations in the system by one.

Again, a S gives you one solution to the system of equations. If there are several solutions, you can use H a S to get a general family of solutions, or, if there is a finite number of solutions, you can use a P to get a list. (In the latter case, the result will take the form of a matrix where the rows are different solutions and the columns correspond to the variables you requested.)

Another way to deal with certain kinds of overdetermined systems of equations is the a F command, which does least-squares fitting to satisfy the equations. See section 12.8 Curve Fitting.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.6.3 Decomposing Polynomials

The poly function takes a polynomial and a variable as arguments, and returns a vector of polynomial coefficients (constant coefficient first). For example, `poly(x^3 + 2 x, x)' returns [0, 2, 0, 1]. If the input is not a polynomial in x, the call to poly is left in symbolic form. If the input does not involve the variable x, the input is returned in a list of length one, representing a polynomial with only a constant coefficient. The call `poly(x, x)' returns the vector [0, 1]. The last element of the returned vector is guaranteed to be nonzero; note that `poly(0, x)' returns the empty vector []. Note also that x may actually be any formula; for example, `poly(sin(x)^2 - sin(x) + 3, sin(x))' returns [3, -1, 1].

To get the x^k coefficient of polynomial p, use `poly(p, x)_(k+1)'. To get the degree of polynomial p, use `vlen(poly(p, x)) - 1'. For example, `poly((x+1)^4, x)' returns `[1, 4, 6, 4, 1]', so `poly((x+1)^4, x)_(2+1)' gives the x^2 coefficient of this polynomial, 6.

One important feature of the solver is its ability to recognize formulas which are "essentially" polynomials. This ability is made available to the user through the gpoly function, which is used just like poly: `gpoly(expr, var)'. If expr is a polynomial in some term which includes var, then this function will return a vector `[x, c, a]' where x is the term that depends on var, c is a vector of polynomial coefficients (like the one returned by poly), and a is a multiplier which is usually 1. Basically, `expr = a*(c_1 + c_2 x + c_3 x^2 + ...)'. The last element of c is guaranteed to be non-zero, and c will not equal `[1]' (i.e., the trivial decomposition expr = x is not considered a polynomial). One side effect is that `gpoly(x, x)' and `gpoly(6, x)', both of which might be expected to recognize their arguments as polynomials, will not because the decomposition is considered trivial.

For example, `gpoly((x-2)^2, x)' returns `[x, [4, -4, 1], 1]', since the expanded form of this polynomial is 4 - 4 x + x^2.

The term x may itself be a polynomial in var. This is done to reduce the size of the c vector. For example, `gpoly(x^4 + x^2 - 1, x)' returns `[x^2, [-1, 1, 1], 1]', since a quadratic polynomial in x^2 is easier to solve than a quartic polynomial in x.

A few more examples of the kinds of polynomials gpoly can discover:

sin(x) - 1               [sin(x), [-1, 1], 1]
x + 1/x - 1              [x, [1, -1, 1], 1/x]
x + 1/x                  [x^2, [1, 1], 1/x]
x^3 + 2 x                [x^2, [2, 1], x]
x + x^2:3 + sqrt(x)      [x^1:6, [1, 1, 0, 1], x^1:2]
x^(2a) + 2 x^a + 5       [x^a, [5, 2, 1], 1]
(exp(-x) + exp(x)) / 2   [e^(2 x), [0.5, 0.5], e^-x]

The poly and gpoly functions accept a third integer argument which specifies the largest degree of polynomial that is acceptable. If this is n, then only c vectors of length n+1 or less will be returned. Otherwise, the poly or gpoly call will remain in symbolic form. For example, the equation solver can handle quartics and smaller polynomials, so it calls `gpoly(expr, var, 4)' to discover whether expr can be treated by its linear, quadratic, cubic, or quartic formulas.

The pdeg function computes the degree of a polynomial; `pdeg(p,x)' is the highest power of x that appears in p. This is the same as `vlen(poly(p,x))-1', but is much more efficient. If p is constant with respect to x, then `pdeg(p,x) = 0'. If p is not a polynomial in x (e.g., `pdeg(2 cos(x), x)', the function remains unevaluated. It is possible to omit the second argument x, in which case `pdeg(p)' returns the highest total degree of any term of the polynomial, counting all variables that appear in p. Note that pdeg(c) = pdeg(c,x) = 0 for any nonzero constant c; the degree of the constant zero is considered to be -inf (minus infinity).

The plead function finds the leading term of a polynomial. Thus `plead(p,x)' is equivalent to `poly(p,x)_vlen(poly(p,x))', though again more efficient. In particular, `plead((2x+1)^10, x)' returns 1024 without expanding out the list of coefficients. The value of plead(p,x) will be zero only if p = 0.

The pcont function finds the content of a polynomial. This is the greatest common divisor of all the coefficients of the polynomial. With two arguments, pcont(p,x) effectively uses `poly(p,x)' to get a list of coefficients, then uses pgcd (the polynomial GCD function) to combine these into an answer. For example, `pcont(4 x y^2 + 6 x^2 y, x)' is `2 y'. The content is basically the "biggest" polynomial that can be divided into p exactly. The sign of the content is the same as the sign of the leading coefficient.

With only one argument, `pcont(p)' computes the numerical content of the polynomial, i.e., the gcd of the numerical coefficients of all the terms in the formula. Note that gcd is defined on rational numbers as well as integers; it computes the gcd of the numerators and the lcm of the denominators. Thus `pcont(4:3 x y^2 + 6 x^2 y)' returns 2:3. Dividing the polynomial by this number will clear all the denominators, as well as dividing by any common content in the numerators. The numerical content of a polynomial is negative only if all the coefficients in the polynomial are negative.

The pprim function finds the primitive part of a polynomial, which is simply the polynomial divided (using pdiv if necessary) by its content. If the input polynomial has rational coefficients, the result will have integer coefficients in simplest terms.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.7 Numerical Solutions

Not all equations can be solved symbolically. The commands in this section use numerical algorithms that can find a solution to a specific instance of an equation to any desired accuracy. Note that the numerical commands are slower than their algebraic cousins; it is a good idea to try a S before resorting to these commands.

(See section 12.8 Curve Fitting, for some other, more specialized, operations on numerical data.)

12.7.1 Root Finding  
12.7.2 Minimization  
12.7.3 Systems of Equations  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.7.1 Root Finding

The a R (calc-find-root) [root] command finds a numerical solution (or root) of an equation. (This command treats inequalities the same as equations. If the input is any other kind of formula, it is interpreted as an equation of the form X = 0.)

The a R command requires an initial guess on the top of the stack, and a formula in the second-to-top position. It prompts for a solution variable, which must appear in the formula. All other variables that appear in the formula must have assigned values, i.e., when a value is assigned to the solution variable and the formula is evaluated with =, it should evaluate to a number. Any assigned value for the solution variable itself is ignored and unaffected by this command.

When the command completes, the initial guess is replaced on the stack by a vector of two numbers: The value of the solution variable that solves the equation, and the difference between the lefthand and righthand sides of the equation at that value. Ordinarily, the second number will be zero or very nearly zero. (Note that Calc uses a slightly higher precision while finding the root, and thus the second number may be slightly different from the value you would compute from the equation yourself.)

The v h (calc-head) command is a handy way to extract the first element of the result vector, discarding the error term.

The initial guess can be a real number, in which case Calc searches for a real solution near that number, or a complex number, in which case Calc searches the whole complex plane near that number for a solution, or it can be an interval form which restricts the search to real numbers inside that interval.

Calc tries to use a d to take the derivative of the equation. If this succeeds, it uses Newton's method. If the equation is not differentiable Calc uses a bisection method. (If Newton's method appears to be going astray, Calc switches over to bisection if it can, or otherwise gives up. In this case it may help to try again with a slightly different initial guess.) If the initial guess is a complex number, the function must be differentiable.

If the formula (or the difference between the sides of an equation) is negative at one end of the interval you specify and positive at the other end, the root finder is guaranteed to find a root. Otherwise, Calc subdivides the interval into small parts looking for positive and negative values to bracket the root. When your guess is an interval, Calc will not look outside that interval for a root.

The H a R [wroot] command is similar to a R, except that if the initial guess is an interval for which the function has the same sign at both ends, then rather than subdividing the interval Calc attempts to widen it to enclose a root. Use this mode if you are not sure if the function has a root in your interval.

If the function is not differentiable, and you give a simple number instead of an interval as your initial guess, Calc uses this widening process even if you did not type the Hyperbolic flag. (If the function is differentiable, Calc uses Newton's method which does not require a bounding interval in order to work.)

If Calc leaves the root or wroot function in symbolic form on the stack, it will normally display an explanation for why no root was found. If you miss this explanation, press w (calc-why) to get it back.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.7.2 Minimization

The a N (calc-find-minimum) [minimize] command finds a minimum value for a formula. It is very similar in operation to a R (calc-find-root): You give the formula and an initial guess on the stack, and are prompted for the name of a variable. The guess may be either a number near the desired minimum, or an interval enclosing the desired minimum. The function returns a vector containing the value of the variable which minimizes the formula's value, along with the minimum value itself.

Note that this command looks for a local minimum. Many functions have more than one minimum; some, like x sin(x), have infinitely many. In fact, there is no easy way to define the "global" minimum of x sin(x) but Calc can still locate any particular local minimum for you. Calc basically goes downhill from the initial guess until it finds a point at which the function's value is greater both to the left and to the right. Calc does not use derivatives when minimizing a function.

If your initial guess is an interval and it looks like the minimum occurs at one or the other endpoint of the interval, Calc will return that endpoint only if that endpoint is closed; thus, minimizing 17 x over [2..3] will return [2, 38], but minimizing over (2..3] would report no minimum found. In general, you should use closed intervals to find literally the minimum value in that range of x, or open intervals to find the local minimum, if any, that happens to lie in that range.

Most functions are smooth and flat near their minimum values. Because of this flatness, if the current precision is, say, 12 digits, the variable can only be determined meaningfully to about six digits. Thus you should set the precision to twice as many digits as you need in your answer.

The H a N [wminimize] command, analogously to H a R, expands the guess interval to enclose a minimum rather than requiring that the minimum lie inside the interval you supply.

The a X (calc-find-maximum) [maximize] and H a X [wmaximize] commands effectively minimize the negative of the formula you supply.

The formula must evaluate to a real number at all points inside the interval (or near the initial guess if the guess is a number). If the initial guess is a complex number the variable will be minimized over the complex numbers; if it is real or an interval it will be minimized over the reals.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.7.3 Systems of Equations

The a R command can also solve systems of equations. In this case, the equation should instead be a vector of equations, the guess should instead be a vector of numbers (intervals are not supported), and the variable should be a vector of variables. You can omit the brackets while entering the list of variables. Each equation must be differentiable by each variable for this mode to work. The result will be a vector of two vectors: The variable values that solved the system of equations, and the differences between the sides of the equations with those variable values. There must be the same number of equations as variables. Since only plain numbers are allowed as guesses, the Hyperbolic flag has no effect when solving a system of equations.

It is also possible to minimize over many variables with a N (or maximize with a X). Once again the variable name should be replaced by a vector of variables, and the initial guess should be an equal-sized vector of initial guesses. But, unlike the case of multidimensional a R, the formula being minimized should still be a single formula, not a vector. Beware that multidimensional minimization is currently very slow.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8 Curve Fitting

The a F command fits a set of data to a model formula, such as y = m x + b where m and b are parameters to be determined. For a typical set of measured data there will be no single m and b that exactly fit the data; in this case, Calc chooses values of the parameters that provide the closest possible fit.

12.8.1 Linear Fits  
12.8.2 Polynomial and Multilinear Fits  
12.8.3 Error Estimates for Fits  
12.8.4 Standard Nonlinear Models  
12.8.5 Curve Fitting Details  
12.8.6 Polynomial Interpolation  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8.1 Linear Fits

The a F (calc-curve-fit) [fit] command attempts to fit a set of data (x and y vectors of numbers) to a straight line, polynomial, or other function of x. For the moment we will consider only the case of fitting to a line, and we will ignore the issue of whether or not the model was in fact a good fit for the data.

In a standard linear least-squares fit, we have a set of (x,y) data points that we wish to fit to the model y = m x + b by adjusting the parameters m and b to make the y values calculated from the formula be as close as possible to the actual y values in the data set. (In a polynomial fit, the model is instead, say, y = a x^3 + b x^2 + c x + d. In a multilinear fit, we have data points of the form (x_1,x_2,x_3,y) and our model is y = a x_1 + b x_2 + c x_3 + d. These will be discussed later.)

In the model formula, variables like x and x_2 are called the independent variables, and y is the dependent variable. Variables like m, a, and b are called the parameters of the model.

The a F command takes the data set to be fitted from the stack. By default, it expects the data in the form of a matrix. For example, for a linear or polynomial fit, this would be a 2xN matrix where the first row is a list of x values and the second row has the corresponding y values. For the multilinear fit shown above, the matrix would have four rows (x_1, x_2, x_3, and y, respectively).

If you happen to have an Nx2 matrix instead of a 2xN matrix, just press v t first to transpose the matrix.

After you type a F, Calc prompts you to select a model. For a linear fit, press the digit 1.

Calc then prompts for you to name the variables. By default it chooses high letters like x and y for independent variables and low letters like a and b for parameters. (The dependent variable doesn't need a name.) The two kinds of variables are separated by a semicolon. Since you generally care more about the names of the independent variables than of the parameters, Calc also allows you to name only those and let the parameters use default names.

For example, suppose the data matrix

[ [ 1, 2, 3, 4,  5  ]
  [ 5, 7, 9, 11, 13 ] ]

is on the stack and we wish to do a simple linear fit. Type a F, then 1 for the model, then RET to use the default names. The result will be the formula 3 + 2 x on the stack. Calc has created the model expression a + b x, then found the optimal values of a and b to fit the data. (In this case, it was able to find an exact fit.) Calc then substituted those values for a and b in the model formula.

The a F command puts two entries in the trail. One is, as always, a copy of the result that went to the stack; the other is a vector of the actual parameter values, written as equations: [a = 3, b = 2], in case you'd rather read them in a list than pick them out of the formula. (You can type t y to move this vector to the stack; see section 7.3 Trail Commands.)

Specifying a different independent variable name will affect the resulting formula: a F 1 k RET produces 3 + 2 k. Changing the parameter names (say, a F 1 k;b,m RET) will affect the equations that go into the trail.

To see what happens when the fit is not exact, we could change the number 13 in the data matrix to 14 and try the fit again. The result is:

2.6 + 2.2 x

Evaluating this formula, say with v x 5 RET TAB V M $ RET, shows a reasonably close match to the y-values in the data.

[4.8, 7., 9.2, 11.4, 13.6]

Since there is no line which passes through all the N data points, Calc has chosen a line that best approximates the data points using the method of least squares. The idea is to define the chi-square error measure

chi^2 = sum((y_i - (a + b x_i))^2, i, 1, N)

which is clearly zero if a + b x exactly fits all data points, and increases as various a + b x_i values fail to match the corresponding y_i values. There are several reasons why the summand is squared, one of them being to ensure that chi^2 >= 0. Least-squares fitting simply chooses the values of a and b for which the error chi^2 is as small as possible.

Other kinds of models do the same thing but with a different model formula in place of a + b x_i.

A numeric prefix argument causes the a F command to take the data in some other form than one big matrix. A positive argument N will take N items from the stack, corresponding to the N rows of a data matrix. In the linear case, N must be 2 since there is always one independent variable and one dependent variable.

A prefix of zero or plain C-u is a compromise; Calc takes two items from the stack, an N-row matrix of x values, and a vector of y values. If there is only one independent variable, the x values can be either a one-row matrix or a plain vector, in which case the C-u prefix is the same as a C-u 2 prefix.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8.2 Polynomial and Multilinear Fits

To fit the data to higher-order polynomials, just type one of the digits 2 through 9 when prompted for a model. For example, we could fit the original data matrix from the previous section (with 13, not 14) to a parabola instead of a line by typing a F 2 RET.

2.00000000001 x - 1.5e-12 x^2 + 2.99999999999

Note that since the constant and linear terms are enough to fit the data exactly, it's no surprise that Calc chose a tiny contribution for x^2. (The fact that it's not exactly zero is due only to roundoff error. Since our data are exact integers, we could get an exact answer by typing m f first to get fraction mode. Then the x^2 term would vanish altogether. Usually, though, the data being fitted will be approximate floats so fraction mode won't help.)

Doing the a F 2 fit on the data set with 14 instead of 13 gives a much larger x^2 contribution, as Calc bends the line slightly to improve the fit.

0.142857142855 x^2 + 1.34285714287 x + 3.59999999998

An important result from the theory of polynomial fitting is that it is always possible to fit N data points exactly using a polynomial of degree N-1, sometimes called an interpolating polynomial. Using the modified (14) data matrix, a model number of 4 gives a polynomial that exactly matches all five data points:

0.04167 x^4 - 0.4167 x^3 + 1.458 x^2 - 0.08333 x + 4.

The actual coefficients we get with a precision of 12, like 0.0416666663588, clearly suffer from loss of precision. It is a good idea to increase the working precision to several digits beyond what you need when you do a fitting operation. Or, if your data are exact, use fraction mode to get exact results.

You can type i instead of a digit at the model prompt to fit the data exactly to a polynomial. This just counts the number of columns of the data matrix to choose the degree of the polynomial automatically.

Fitting data "exactly" to high-degree polynomials is not always a good idea, though. High-degree polynomials have a tendency to wiggle uncontrollably in between the fitting data points. Also, if the exact-fit polynomial is going to be used to interpolate or extrapolate the data, it is numerically better to use the a p command described below. See section 12.8.6 Polynomial Interpolation.

Another generalization of the linear model is to assume the y values are a sum of linear contributions from several x values. This is a multilinear fit, and it is also selected by the 1 digit key. (Calc decides whether the fit is linear or multilinear by counting the rows in the data matrix.)

Given the data matrix,

[ [  1,   2,   3,    4,   5  ]
  [  7,   2,   3,    5,   2  ]
  [ 14.5, 15, 18.5, 22.5, 24 ] ]

the command a F 1 RET will call the first row x and the second row y, and will fit the values in the third row to the model a + b x + c y.

8. + 3. x + 0.5 y

Calc can do multilinear fits with any number of independent variables (i.e., with any number of data rows).

Yet another variation is homogeneous linear models, in which the constant term is known to be zero. In the linear case, this means the model formula is simply a x; in the multilinear case, the model might be a x + b y + c z; and in the polynomial case, the model could be a x + b x^2 + c x^3. You can get a homogeneous linear or multilinear model by pressing the letter h followed by a regular model key, like 1 or 2.

It is certainly possible to have other constrained linear models, like 2.3 + a x or a - 4 x. While there is no single key to select models like these, a later section shows how to enter any desired model by hand. In the first case, for example, you would enter a F ' 2.3 + a x.

Another class of models that will work but must be entered by hand are multinomial fits, e.g., a + b x + c y + d x^2 + e y^2 + f x y.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8.3 Error Estimates for Fits

With the Hyperbolic flag, H a F [efit] performs the same fitting operation as a F, but reports the coefficients as error forms instead of plain numbers. Fitting our two data matrices (first with 13, then with 14) to a line with H a F gives the results,

3. + 2. x
2.6 +/- 0.382970843103 + 2.2 +/- 0.115470053838 x

In the first case the estimated errors are zero because the linear fit is perfect. In the second case, the errors are nonzero but moderately small, because the data are still very close to linear.

It is also possible for the input to a fitting operation to contain error forms. The data values must either all include errors or all be plain numbers. Error forms can go anywhere but generally go on the numbers in the last row of the data matrix. If the last row contains error forms `y_i +/- sigma_i', then the chi^2 statistic is now,

chi^2 = sum(((y_i - (a + b x_i)) / sigma_i)^2, i, 1, N)

so that data points with larger error estimates contribute less to the fitting operation.

If there are error forms on other rows of the data matrix, all the errors for a given data point are combined; the square root of the sum of the squares of the errors forms the sigma_i used for the data point.

Both a F and H a F can accept error forms in the input matrix, although if you are concerned about error analysis you will probably use H a F so that the output also contains error estimates.

If the input contains error forms but all the sigma_i values are the same, it is easy to see that the resulting fitted model will be the same as if the input did not have error forms at all ( chi^2 is simply scaled uniformly by 1 / sigma^2, which doesn't affect where it has a minimum). But there will be a difference in the estimated errors of the coefficients reported by H a F.

Consult any text on statistical modelling of data for a discussion of where these error estimates come from and how they should be interpreted.

With the Inverse flag, I a F [xfit] produces even more information. The result is a vector of six items:

  1. The model formula with error forms for its coefficients or parameters. This is the result that H a F would have produced.

  2. A vector of "raw" parameter values for the model. These are the polynomial coefficients or other parameters as plain numbers, in the same order as the parameters appeared in the final prompt of the I a F command. For polynomials of degree d, this vector will have length M = d+1 with the constant term first.

  3. The covariance matrix C computed from the fit. This is an MxM symmetric matrix; the diagonal elements C_j_j are the variances sigma_j^2 of the parameters. The other elements are covariances sigma_i_j^2 that describe the correlation between pairs of parameters. (A related set of numbers, the linear correlation coefficients r_i_j, are defined as sigma_i_j^2 / sigma_i sigma_j.)

  4. A vector of M "parameter filter" functions whose meanings are described below. If no filters are necessary this will instead be an empty vector; this is always the case for the polynomial and multilinear fits described so far.

  5. The value of chi^2 for the fit, calculated by the formulas shown above. This gives a measure of the quality of the fit; statisticians consider chi^2 = N - M to indicate a moderately good fit (where again N is the number of data points and M is the number of parameters).

  6. A measure of goodness of fit expressed as a probability Q. This is computed from the utpc probability distribution function using chi^2 with N - M degrees of freedom. A value of 0.5 implies a good fit; some texts recommend that often Q = 0.1 or even 0.001 can signify an acceptable fit. In particular, chi^2 statistics assume the errors in your inputs follow a normal (Gaussian) distribution; if they don't, you may have to accept smaller values of Q.

    The Q value is computed only if the input included error estimates. Otherwise, Calc will report the symbol nan for Q. The reason is that in this case the chi^2 value has effectively been used to estimate the original errors in the input, and thus there is no redundant information left over to use for a confidence test.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8.4 Standard Nonlinear Models

The a F command also accepts other kinds of models besides lines and polynomials. Some common models have quick single-key abbreviations; others must be entered by hand as algebraic formulas.

Here is a complete list of the standard models recognized by a F:

Linear or multilinear. a + b x + c y + d z.
Polynomials. a + b x + c x^2 + d x^3.
Exponential. a exp(b x) exp(c y).
Base-10 exponential. a 10^(b x) 10^(c y).
Exponential (alternate notation). exp(a + b x + c y).
Base-10 exponential (alternate). 10^(a + b x + c y).
Logarithmic. a + b ln(x) + c ln(y).
Base-10 logarithmic. a + b log10(x) + c log10(y).
General exponential. a b^x c^y.
Power law. a x^b y^c.
Quadratic. a + b (x-c)^2 + d (x-e)^2.
Gaussian. (a / b sqrt(2 pi)) exp(-0.5*((x-c)/b)^2).

All of these models are used in the usual way; just press the appropriate letter at the model prompt, and choose variable names if you wish. The result will be a formula as shown in the above table, with the best-fit values of the parameters substituted. (You may find it easier to read the parameter values from the vector that is placed in the trail.)

All models except Gaussian and polynomials can generalize as shown to any number of independent variables. Also, all the built-in models have an additive or multiplicative parameter shown as a in the above table which can be replaced by zero or one, as appropriate, by typing h before the model key.

Note that many of these models are essentially equivalent, but express the parameters slightly differently. For example, a b^x and the other two exponential models are all algebraic rearrangements of each other. Also, the "quadratic" model is just a degree-2 polynomial with the parameters expressed differently. Use whichever form best matches the problem.

The HP-28/48 calculators support four different models for curve fitting, called LIN, LOG, EXP, and PWR. These correspond to Calc models `a + b x', `a + b ln(x)', `a exp(b x)', and `a x^b', respectively. In each case, a is what the HP-48 identifies as the "intercept," and b is what it calls the "slope."

If the model you want doesn't appear on this list, press ' (the apostrophe key) at the model prompt to enter any algebraic formula, such as m x - b, as the model. (Not all models will work, though--see the next section for details.)

The model can also be an equation like y = m x + b. In this case, Calc thinks of all the rows of the data matrix on equal terms; this model effectively has two parameters (m and b) and two independent variables (x and y), with no "dependent" variables. Model equations do not need to take this y = form. For example, the implicit line equation a x + b y = 1 works fine as a model.

When you enter a model, Calc makes an alphabetical list of all the variables that appear in the model. These are used for the default parameters, independent variables, and dependent variable (in that order). If you enter a plain formula (not an equation), Calc assumes the dependent variable does not appear in the formula and thus does not need a name.

For example, if the model formula has the variables a,mu,sigma,t,x, and the data matrix has three rows (meaning two independent variables), Calc will use a,mu,sigma as the default parameters, and the data rows will be named t and x, respectively. If you enter an equation instead of a plain formula, Calc will use a,mu as the parameters, and sigma,t,x as the three independent variables.

You can, of course, override these choices by entering something different at the prompt. If you leave some variables out of the list, those variables must have stored values and those stored values will be used as constants in the model. (Stored values for the parameters and independent variables are ignored by the a F command.) If you list only independent variables, all the remaining variables in the model formula will become parameters.

If there are $ signs in the model you type, they will stand for parameters and all other variables (in alphabetical order) will be independent. Use $ for one parameter, $$ for another, and so on. Thus $ x + $$ is another way to describe a linear model.

If you type a $ instead of ' at the model prompt itself, Calc will take the model formula from the stack. (The data must then appear at the second stack level.) The same conventions are used to choose which variables in the formula are independent by default and which are parameters.

Models taken from the stack can also be expressed as vectors of two or three elements, [model, vars] or [model, vars, params]. Each of vars and params may be either a variable or a vector of variables. (If params is omitted, all variables in model except those listed as vars are parameters.)

When you enter a model manually with ', Calc puts a 3-vector describing the model in the trail so you can get it back if you wish.

Finally, you can store a model in one of the Calc variables Model1 or Model2, then use this model by typing a F u or a F U (respectively). The value stored in the variable can be any of the formats that a F $ would accept for a model on the stack.

Calc uses the principal values of inverse functions like ln and arcsin when doing fits. For example, when you enter the model `y = sin(a t + b)' Calc actually uses the easier form `arcsin(y) = a t + b'. The arcsin function always returns results in the range from -90 to 90 degrees (or the equivalent range in radians). Suppose you had data that you believed to represent roughly three oscillations of a sine wave, so that the argument of the sine might go from zero to 3*360 degrees. The above model would appear to be a good way to determine the true frequency and phase of the sine wave, but in practice it would fail utterly. The righthand side of the actual model `arcsin(y) = a t + b' will grow smoothly with t, but the lefthand side will bounce back and forth between -90 and 90. No values of a and b can make the two sides match, even approximately.

There is no good solution to this problem at present. You could restrict your data to small enough ranges so that the above problem doesn't occur (i.e., not straddling any peaks in the sine wave). Or, in this case, you could use a totally different method such as Fourier analysis, which is beyond the scope of the a F command. (Unfortunately, Calc does not currently have any facilities for taking Fourier and related transforms.)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8.5 Curve Fitting Details

Calc's internal least-squares fitter can only handle multilinear models. More precisely, it can handle any model of the form a f(x,y,z) + b g(x,y,z) + c h(x,y,z), where a,b,c are the parameters and x,y,z are the independent variables (of course there can be any number of each, not just three).

In a simple multilinear or polynomial fit, it is easy to see how to convert the model into this form. For example, if the model is a + b x + c x^2, then f(x) = 1, g(x) = x, and h(x) = x^2 are suitable functions.

For other models, Calc uses a variety of algebraic manipulations to try to put the problem into the form

Y(x,y,z) = A(a,b,c) F(x,y,z) + B(a,b,c) G(x,y,z) + C(a,b,c) H(x,y,z)

where Y,A,B,C,F,G,H are arbitrary functions. It computes Y, F, G, and H for all the data points, does a standard linear fit to find the values of A, B, and C, then uses the equation solver to solve for a,b,c in terms of A,B,C.

A remarkable number of models can be cast into this general form. We'll look at two examples here to see how it works. The power-law model y = a x^b with two independent variables and two parameters can be rewritten as follows:

y = a x^b
y = a exp(b ln(x))
y = exp(ln(a) + b ln(x))
ln(y) = ln(a) + b ln(x)

which matches the desired form with Y = ln(y), A = ln(a), F = 1, B = b, and G = ln(x). Calc thus computes the logarithms of your y and x values, does a linear fit for A and B, then solves to get a = exp(A) and b = B.

Another interesting example is the "quadratic" model, which can be handled by expanding according to the distributive law.

y = a + b*(x - c)^2
y = a + b c^2 - 2 b c x + b x^2

which matches with Y = y, A = a + b c^2, F = 1, B = -2 b c, G = x (the -2 factor could just as easily have been put into G instead of B), C = b, and H = x^2.

The Gaussian model looks quite complicated, but a closer examination shows that it's actually similar to the quadratic model but with an exponential that can be brought to the top and moved into Y.

An example of a model that cannot be put into general linear form is a Gaussian with a constant background added on, i.e., d + the regular Gaussian formula. If you have a model like this, your best bet is to replace enough of your parameters with constants to make the model linearizable, then adjust the constants manually by doing a series of fits. You can compare the fits by graphing them, by examining the goodness-of-fit measures returned by I a F, or by some other method suitable to your application. Note that some models can be linearized in several ways. The Gaussian-plus-d model can be linearized by setting d (the background) to a constant, or by setting b (the standard deviation) and c (the mean) to constants.

To fit a model with constants substituted for some parameters, just store suitable values in those parameter variables, then omit them from the list of parameters when you answer the variables prompt.

A last desperate step would be to use the general-purpose minimize function rather than fit. After all, both functions solve the problem of minimizing an expression (the chi^2 sum) by adjusting certain parameters in the expression. The a F command is able to use a vastly more efficient algorithm due to its special knowledge about linear chi-square sums, but the a N command can do the same thing by brute force.

A compromise would be to pick out a few parameters without which the fit is linearizable, and use minimize on a call to fit which efficiently takes care of the rest of the parameters. The thing to be minimized would be the value of chi^2 returned as the fifth result of the xfit function:

minimize(xfit(gaus(a,b,c,d,x), x, [a,b,c], data)_5, d, guess)

where gaus represents the Gaussian model with background, data represents the data matrix, and guess represents the initial guess for d that minimize requires. This operation will only be, shall we say, extraordinarily slow rather than astronomically slow (as would be the case if minimize were used by itself to solve the problem).

The I a F [xfit] command is somewhat trickier when nonlinear models are used. The second item in the result is the vector of "raw" parameters A, B, C. The covariance matrix is written in terms of those raw parameters. The fifth item is a vector of filter expressions. This is the empty vector `[]' if the raw parameters were the same as the requested parameters, i.e., if A = a, B = b, and so on (which is always true if the model is already linear in the parameters as written, e.g., for polynomial fits). If the parameters had to be rearranged, the fifth item is instead a vector of one formula per parameter in the original model. The raw parameters are expressed in these "filter" formulas as `fitdummy(1)' for A, `fitdummy(2)' for B, and so on.

When Calc needs to modify the model to return the result, it replaces `fitdummy(1)' in all the filters with the first item in the raw parameters list, and so on for the other raw parameters, then evaluates the resulting filter formulas to get the actual parameter values to be substituted into the original model. In the case of H a F and I a F where the parameters must be error forms, Calc uses the square roots of the diagonal entries of the covariance matrix as error values for the raw parameters, then lets Calc's standard error-form arithmetic take it from there.

If you use I a F with a nonlinear model, be sure to remember that the covariance matrix is in terms of the raw parameters, not the actual requested parameters. It's up to you to figure out how to interpret the covariances in the presence of nontrivial filter functions.

Things are also complicated when the input contains error forms. Suppose there are three independent and dependent variables, x, y, and z, one or more of which are error forms in the data. Calc combines all the error values by taking the square root of the sum of the squares of the errors. It then changes x and y to be plain numbers, and makes z into an error form with this combined error. The Y(x,y,z) part of the linearized model is evaluated, and the result should be an error form. The error part of that result is used for sigma_i for the data point. If for some reason Y(x,y,z) does not return an error form, the combined error from z is used directly for sigma_i. Finally, z is also stripped of its error for use in computing F(x,y,z), G(x,y,z) and so on; the righthand side of the linearized model is computed in regular arithmetic with no error forms.

(While these rules may seem complicated, they are designed to do the most reasonable thing in the typical case that Y(x,y,z) depends only on the dependent variable z, and in fact is often simply equal to z. For common cases like polynomials and multilinear models, the combined error is simply used as the sigma for the data point with no further ado.)

It may be the case that the model you wish to use is linearizable, but Calc's built-in rules are unable to figure it out. Calc uses its algebraic rewrite mechanism to linearize a model. The rewrite rules are kept in the variable FitRules. You can edit this variable using the s e FitRules command; in fact, there is a special s F command just for editing FitRules. See section 14.3 Other Operations on Variables.

See section 12.11 Rewrite Rules, for a discussion of rewrite rules.

Calc uses FitRules as follows. First, it converts the model to an equation if necessary and encloses the model equation in a call to the function fitmodel (which is not actually a defined function in Calc; it is only used as a placeholder by the rewrite rules). Parameter variables are renamed to function calls `fitparam(1)', `fitparam(2)', and so on, and independent variables are renamed to `fitvar(1)', `fitvar(2)', etc. The dependent variable is the highest-numbered fitvar. For example, the power law model a x^b is converted to y = a x^b, then to

fitmodel(fitvar(2) = fitparam(1) fitvar(1)^fitparam(2))

Calc then applies the rewrites as if by `C-u 0 a r FitRules'. (The zero prefix means that rewriting should continue until no further changes are possible.)

When rewriting is complete, the fitmodel call should have been replaced by a fitsystem call that looks like this:

fitsystem(Y, FGH, abc)

where Y is a formula that describes the function Y(x,y,z), FGH is the vector of formulas [F(x,y,z), G(x,y,z), H(x,y,z)], and abc is the vector of parameter filters which refer to the raw parameters as `fitdummy(1)' for A, `fitdummy(2)' for B, etc. While the number of raw parameters (the length of the FGH vector) is usually the same as the number of original parameters (the length of the abc vector), this is not required.

The power law model eventually boils down to

          [1, ln(fitvar(1))],
          [exp(fitdummy(1)), fitdummy(2)])

The actual implementation of FitRules is complicated; it proceeds in four phases. First, common rearrangements are done to try to bring linear terms together and to isolate functions like exp and ln either all the way "out" (so that they can be put into Y) or all the way "in" (so that they can be put into abc or FGH). In particular, all non-constant powers are converted to logs-and-exponentials form, and the distributive law is used to expand products of sums. Quotients are rewritten to use the `fitinv' function, where `fitinv(x)' represents 1/x while the FitRules are operating. (The use of fitinv makes recognition of linear-looking forms easier.) If you modify FitRules, you will probably only need to modify the rules for this phase.

Phase two, whose rules can actually also apply during phases one and three, first rewrites fitmodel to a two-argument form `fitmodel(Y, model)', where Y is initially zero and model has been changed from a=b to a-b form. It then tries to peel off invertible functions from the outside of model and put them into Y instead, calling the equation solver to invert the functions. Finally, when this is no longer possible, the fitmodel is changed to a four-argument fitsystem, where the fourth argument is model and the FGH and abc vectors are initially empty. (The last vector is really ABC, corresponding to raw parameters, for now.)

Phase three converts a sum of items in the model to a sum of `fitpart(a, b, c)' terms which represent terms `a*b*c' of the sum, where a is all factors that do not involve any variables, b is all factors that involve only parameters, and c is the factors that involve only independent variables. (If this decomposition is not possible, the rule set will not complete and Calc will complain that the model is too complex.) Then fitparts with equal b or c components are merged back together using the distributive law in order to minimize the number of raw parameters needed.

Phase four moves the fitpart terms into the FGH and ABC vectors. Also, some of the algebraic expansions that were done in phase 1 are undone now to make the formulas more computationally efficient. Finally, it calls the solver one more time to convert the ABC vector to an abc vector, and removes the fourth model argument (which by now will be zero) to obtain the three-argument fitsystem that the linear least-squares solver wants to see.

Two functions which are useful in connection with FitRules are `hasfitparams(x)' and `hasfitvars(x)', which check whether x refers to any parameters or independent variables, respectively. Specifically, these functions return "true" if the argument contains any fitparam (or fitvar) function calls, and "false" otherwise. (Recall that "true" means a nonzero number, and "false" means zero. The actual nonzero number returned is the largest n from all the `fitparam(n)'s or `fitvar(n)'s, respectively, that appear in the formula.)

The fit function in algebraic notation normally takes four arguments, `fit(model, vars, params, data)', where model is the model formula as it would be typed after a F ', vars is the independent variable or a vector of independent variables, params likewise gives the parameter(s), and data is the data matrix. Note that the length of vars must be equal to the number of rows in data if model is an equation, or one less than the number of rows if model is a plain formula. (Actually, a name for the dependent variable is allowed but will be ignored in the plain-formula case.)

If params is omitted, the parameters are all variables in model except those that appear in vars. If vars is also omitted, Calc sorts all the variables that appear in model alphabetically and uses the higher ones for vars and the lower ones for params.

Alternatively, `fit(modelvec, data)' is allowed where modelvec is a 2- or 3-vector describing the model and variables, as discussed previously.

If Calc is unable to do the fit, the fit function is left in symbolic form, ordinarily with an explanatory message. The message will be "Model expression is too complex" if the linearizer was unable to put the model into the required form.

The efit (corresponding to H a F) and xfit (for I a F) functions are completely analogous.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.8.6 Polynomial Interpolation

The a p (calc-poly-interp) [polint] command does a polynomial interpolation at a particular x value. It takes two arguments from the stack: A data matrix of the sort used by a F, and a single number which represents the desired x value. Calc effectively does an exact polynomial fit as if by a F i, then substitutes the x value into the result in order to get an approximate y value based on the fit. (Calc does not actually use a F i, however; it uses a direct method which is both more efficient and more numerically stable.)

The result of a p is actually a vector of two values: The y value approximation, and an error measure dy that reflects Calc's estimation of the probable error of the approximation at that value of x. If the input x is equal to any of the x values in the data matrix, the output y will be the corresponding y value from the matrix, and the output dy will be exactly zero.

A prefix argument of 2 causes a p to take separate x- and y-vectors from the stack instead of one data matrix.

If x is a vector of numbers, a p will return a matrix of interpolated results for each of those x values. (The matrix will have two columns, the y values and the dy values.) If x is a formula instead of a number, the polint function remains in symbolic form; use the a " command to expand it out to a formula that describes the fit in symbolic terms.

In all cases, the a p command leaves the data vectors or matrix on the stack. Only the x value is replaced by the result.

The H a p [ratint] command does a rational function interpolation. It is used exactly like a p, except that it uses as its model the quotient of two polynomials. If there are N data points, the numerator and denominator polynomials will each have degree N/2 (if N is odd, the denominator will have degree one higher than the numerator).

Rational approximations have the advantage that they can accurately describe functions that have poles (points at which the function's value goes to infinity, so that the denominator polynomial of the approximation goes to zero). If x corresponds to a pole of the fitted rational function, then the result will be a division by zero. If Infinite mode is enabled, the result will be `[uinf, uinf]'.

There is no way to get the actual coefficients of the rational function used by H a p. (The algorithm never generates these coefficients explicitly, and quotients of polynomials are beyond a F's capabilities to fit.)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.9 Summations

The a + (calc-summation) [sum] command computes the sum of a formula over a certain range of index values. The formula is taken from the top of the stack; the command prompts for the name of the summation index variable, the lower limit of the sum (any formula), and the upper limit of the sum. If you enter a blank line at any of these prompts, that prompt and any later ones are answered by reading additional elements from the stack. Thus, ' k^2 RET ' k RET 1 RET 5 RET a + RET produces the result 55.

The choice of index variable is arbitrary, but it's best not to use a variable with a stored value. In particular, while i is often a favorite index variable, it should be avoided in Calc because i has the imaginary constant (0, 1) as a value. If you pressed = on a sum over i, it would be changed to a nonsensical sum over the "variable" (0, 1)! If you really want to use i as an index variable, use s u i RET first to "unstore" this variable. (See section 14.1 Storing Variables.)

A numeric prefix argument steps the index by that amount rather than by one. Thus ' a_k RET C-u -2 a + k RET 10 RET 0 RET yields `a_10 + a_8 + a_6 + a_4 + a_2 + a_0'. A prefix argument of plain C-u causes a + to prompt for the step value, in which case you can enter any formula or enter a blank line to take the step value from the stack. With the C-u prefix, a + can take up to five arguments from the stack: The formula, the variable, the lower limit, the upper limit, and (at the top of the stack), the step value.

Calc knows how to do certain sums in closed form. For example, `sum(6 k^2, k, 1, n) = 2 n^3 + 3 n^2 + n'. In particular, this is possible if the formula being summed is polynomial or exponential in the index variable. Sums of logarithms are transformed into logarithms of products. Sums of trigonometric and hyperbolic functions are transformed to sums of exponentials and then done in closed form. Also, of course, sums in which the lower and upper limits are both numbers can always be evaluated just by grinding them out, although Calc will use closed forms whenever it can for the sake of efficiency.

The notation for sums in algebraic formulas is `sum(expr, var, low, high, step)'. If step is omitted, it defaults to one. If high is omitted, low is actually the upper limit and the lower limit is one. If low is also omitted, the limits are `-inf' and `inf', respectively.

Infinite sums can sometimes be evaluated: `sum(.5^k, k, 1, inf)' returns 1. This is done by evaluating the sum in closed form (to `1. - 0.5^n' in this case), then evaluating this formula with n set to inf. Calc's usual rules for "infinite" arithmetic can find the answer from there. If infinite arithmetic yields a `nan', or if the sum cannot be solved in closed form, Calc leaves the sum function in symbolic form. See section 6.5 Infinities.

As a special feature, if the limits are infinite (or omitted, as described above) but the formula includes vectors subscripted by expressions that involve the iteration variable, Calc narrows the limits to include only the range of integers which result in legal subscripts for the vector. For example, the sum `sum(k [a,b,c,d,e,f,g]_(2k),k)' evaluates to `b + 2 d + 3 f'.

The limits of a sum do not need to be integers. For example, `sum(a_k, k, 0, 2 n, n)' produces `a_0 + a_n + a_(2 n)'. Calc computes the number of iterations using the formula `1 + (high - low) / step', which must, after simplification as if by a s, evaluate to an integer.

If the number of iterations according to the above formula does not come out to an integer, the sum is illegal and will be left in symbolic form. However, closed forms are still supplied, and you are on your honor not to misuse the resulting formulas by substituting mismatched bounds into them. For example, `sum(k, k, 1, 10, 2)' is invalid, but Calc will go ahead and evaluate the closed form solution for the limits 1 and 10 to get the rather dubious answer, 29.25.

If the lower limit is greater than the upper limit (assuming a positive step size), the result is generally zero. However, Calc only guarantees a zero result when the upper limit is exactly one step less than the lower limit, i.e., if the number of iterations is -1. Thus `sum(f(k), k, n, n-1)' is zero but the sum from `n' to `n-2' may report a nonzero value if Calc used a closed form solution.

Calc's logical predicates like a < b return 1 for "true" and 0 for "false." See section 12.10 Logical Operations. This can be used to advantage for building conditional sums. For example, `sum(prime(k)*k^2, k, 1, 20)' is the sum of the squares of all prime numbers from 1 to 20; the prime predicate returns 1 if its argument is prime and 0 otherwise. You can read this expression as "the sum of k^2, where k is prime." Indeed, `sum(prime(k)*k^2, k)' would represent the sum of all primes squared, since the limits default to plus and minus infinity, but there are no such sums that Calc's built-in rules can do in closed form.

As another example, `sum((k != k_0) * f(k), k, 1, n)' is the sum of f(k) for all k from 1 to n, excluding one value k_0. Slightly more tricky is the summand `(k != k_0) / (k - k_0)', which is an attempt to describe the sum of all 1/(k-k_0) except at k = k_0, where this would be a division by zero. But at k = k_0, this formula works out to the indeterminate form 0 / 0, which Calc will not assume is zero. Better would be to use `(k != k_0) ? 1/(k-k_0) : 0'; the `? :' operator does an "if-then-else" test: This expression says, "if k != k_0, then 1/(k-k_0), else zero." Now the formula 1/(k-k_0) will not even be evaluated by Calc when k = k_0.

The a - (calc-alt-summation) [asum] command computes an alternating sum. Successive terms of the sequence are given alternating signs, with the first term (corresponding to the lower index value) being positive. Alternating sums are converted to normal sums with an extra term of the form `(-1)^(k-low)'. This formula is adjusted appropriately if the step value is other than one. For example, the Taylor series for the sine function is `asum(x^k / k!, k, 1, inf, 2)'. (Calc cannot evaluate this infinite series, but it can approximate it if you replace inf with any particular odd number.) Calc converts this series to a regular sum with a step of one, namely `sum((-1)^k x^(2k+1) / (2k+1)!, k, 0, inf)'.

The a * (calc-product) [prod] command is the analogous way to take a product of many terms. Calc also knows some closed forms for products, such as `prod(k, k, 1, n) = n!'. Conditional products can be written `prod(k^prime(k), k, 1, n)' or `prod(prime(k) ? k : 1, k, 1, n)'.

The a T (calc-tabulate) [table] command evaluates a formula at a series of iterated index values, just like sum and prod, but its result is simply a vector of the results. For example, `table(a_i, i, 1, 7, 2)' produces `[a_1, a_3, a_5, a_7]'.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.10 Logical Operations

The following commands and algebraic functions return true/false values, where 1 represents "true" and 0 represents "false." In cases where a truth value is required (such as for the condition part of a rewrite rule, or as the condition for a Z [ Z ] control structure), any nonzero value is accepted to mean "true." (Specifically, anything for which dnonzero returns 1 is "true," and anything for which dnonzero returns 0 or cannot decide is assumed "false." Note that this means that Z [ Z ] will execute the "then" portion if its condition is provably true, but it will execute the "else" portion for any condition like a = b that is not provably true, even if it might be true. Algebraic functions that have conditions as arguments, like ? : and &&, remain unevaluated if the condition is neither provably true nor provably false. See section 8.6 Declarations.)

The a = (calc-equal-to) command, or `eq(a,b)' function (which can also be written `a = b' or `a == b' in an algebraic formula) is true if a and b are equal, either because they are identical expressions, or because they are numbers which are numerically equal. (Thus the integer 1 is considered equal to the float 1.0.) If the equality of a and b cannot be determined, the comparison is left in symbolic form. Note that as a command, this operation pops two values from the stack and pushes back either a 1 or a 0, or a formula `a = b' if the values' equality cannot be determined.

Many Calc commands use `=' formulas to represent equations. For example, the a S (calc-solve-for) command rearranges an equation to solve for a given variable. The a M (calc-map-equation) command can be used to apply any function to both sides of an equation; for example, 2 a M * multiplies both sides of the equation by two. Note that just 2 * would not do the same thing; it would produce the formula `2 (a = b)' which represents 2 if the equality is true or zero if not.

The eq function with more than two arguments (e.g., C-u 3 a = or `a = b = c') tests if all of its arguments are equal. In algebraic notation, the `=' operator is unusual in that it is neither left- nor right-associative: `a = b = c' is not the same as `(a = b) = c' or `a = (b = c)' (which each compare one variable with the 1 or 0 that results from comparing two other variables).

The a # (calc-not-equal-to) command, or `neq(a,b)' or `a != b' function, is true if a and b are not equal. This also works with more than two arguments; `a != b != c != d' tests that all four of a, b, c, and d are distinct numbers.

The a < (calc-less-than) [`lt(a,b)' or `a < b'] operation is true if a is less than b. Similar functions are a > (calc-greater-than) [`gt(a,b)' or `a > b'], a [ (calc-less-equal) [`leq(a,b)' or `a <= b'], and a ] (calc-greater-equal) [`geq(a,b)' or `a >= b'].

While the inequality functions like lt do not accept more than two arguments, the syntax `a <= b < c' is translated to an equivalent expression involving intervals: `b in [a .. c)'. (See the description of in below.) All four combinations of `<' and `<=' are allowed, or any of the four combinations of `>' and `>='. Four-argument constructions like `a < b < c < d', and mixtures like `a < b = c' that involve both equalities and inequalities, are not allowed.

The a . (calc-remove-equal) [rmeq] command extracts the righthand side of the equation or inequality on the top of the stack. It also works elementwise on vectors. For example, if `[x = 2.34, y = z / 2]' is on the stack, then a . produces `[2.34, z / 2]'. As a special case, if the righthand side is a variable and the lefthand side is a number (as in `2.34 = x'), then Calc keeps the lefthand side instead. Finally, this command works with assignments `x := 2.34' as well as equations, always taking the the righthand side, and for `=>' (evaluates-to) operators, always taking the lefthand side.

The a & (calc-logical-and) [`land(a,b)' or `a && b'] function is true if both of its arguments are true, i.e., are non-zero numbers. In this case, the result will be either a or b, chosen arbitrarily. If either argument is zero, the result is zero. Otherwise, the formula is left in symbolic form.

The a | (calc-logical-or) [`lor(a,b)' or `a || b'] function is true if either or both of its arguments are true (nonzero). The result is whichever argument was nonzero, choosing arbitrarily if both are nonzero. If both a and b are zero, the result is zero.

The a ! (calc-logical-not) [`lnot(a)' or `! a'] function is true if a is false (zero), or false if a is true (nonzero). It is left in symbolic form if a is not a number.

The a : (calc-logical-if) [`if(a,b,c)' or `a ? b : c'] function is equal to either b or c if a is a nonzero number or zero, respectively. If a is not a number, the test is left in symbolic form and neither b nor c is evaluated in any way. In algebraic formulas, this is one of the few Calc functions whose arguments are not automatically evaluated when the function itself is evaluated. The others are lambda, quote, and condition.

One minor surprise to watch out for is that the formula `a?3:4' will not work because the `3:4' is parsed as a fraction instead of as three separate symbols. Type something like `a ? 3 : 4' or `a?(3):4' instead.

As a special case, if a evaluates to a vector, then both b and c are evaluated; the result is a vector of the same length as a whose elements are chosen from corresponding elements of b and c according to whether each element of a is zero or nonzero. Each of b and c must be either a vector of the same length as a, or a non-vector which is matched with all elements of a.

The a { (calc-in-set) [`in(a,b)'] function is true if the number a is in the set of numbers represented by b. If b is an interval form, a must be one of the values encompassed by the interval. If b is a vector, a must be equal to one of the elements of the vector. (If any vector elements are intervals, a must be in any of the intervals.) If b is a plain number, a must be numerically equal to b. See section 11.6 Set Operations using Vectors, for a group of commands that manipulate sets of this sort.

The `typeof(a)' function produces an integer or variable which characterizes a. If a is a number, vector, or variable, the result will be one of the following numbers:

 1   Integer
 2   Fraction
 3   Floating-point number
 4   HMS form
 5   Rectangular complex number
 6   Polar complex number
 7   Error form
 8   Interval form
 9   Modulo form
10   Date-only form
11   Date/time form
12   Infinity (inf, uinf, or nan)
100  Variable
101  Vector (but not a matrix)
102  Matrix

Otherwise, a is a formula, and the result is a variable which represents the name of the top-level function call.

The `integer(a)' function returns true if a is an integer. The `real(a)' function is true if a is a real number, either integer, fraction, or float. The `constant(a)' function returns true if a is any of the objects for which typeof would produce an integer code result except for variables, and provided that the components of an object like a vector or error form are themselves constant. Note that infinities do not satisfy any of these tests, nor do special constants like pi and e.

See section 8.6 Declarations, for a set of similar functions that recognize formulas as well as actual numbers. For example, `dint(floor(x))' is true because `floor(x)' is provably integer-valued, but `integer(floor(x))' does not because `floor(x)' is not literally an integer constant.

The `refers(a,b)' function is true if the variable (or sub-expression) b appears in a, or false otherwise. Unlike the other tests described here, this function returns a definite "no" answer even if its arguments are still in symbolic form. The only case where refers will be left unevaluated is if a is a plain variable (different from b).

The `negative(a)' function returns true if a "looks" negative, because it is a negative number, because it is of the form -x, or because it is a product or quotient with a term that looks negative. This is most useful in rewrite rules. Beware that `negative(a)' evaluates to 1 or 0 for any argument a, so it can only be stored in a formula if the default simplifications are turned off first with m O (or if it appears in an unevaluated context such as a rewrite rule condition).

The `variable(a)' function is true if a is a variable, or false if not. If a is a function call, this test is left in symbolic form. Built-in variables like pi and inf are considered variables like any others by this test.

The `nonvar(a)' function is true if a is a non-variable. If its argument is a variable it is left unsimplified; it never actually returns zero. However, since Calc's condition-testing commands consider "false" anything not provably true, this is often good enough.

The functions lin, linnt, islin, and islinnt check if an expression is "linear," i.e., can be written in the form a + b x for some constants a and b, and some variable or subformula x. The function `islin(f,x)' checks if formula f is linear in x, returning 1 if so. For example, `islin(x,x)', `islin(-x,x)', `islin(3,x)', and `islin(x y / 3 - 2, x)' all return 1. The `lin(f,x)' function is similar, except that instead of returning 1 it returns the vector [a, b, x]. For the above examples, this vector would be [0, 1, x], [0, -1, x], [3, 0, x], and [-2, y/3, x], respectively. Both lin and islin generally remain unevaluated for expressions which are not linear, e.g., `lin(2 x^2, x)' and `lin(sin(x), x)'. The second argument can also be a formula; `islin(2 + 3 sin(x), sin(x))' returns true.

The linnt and islinnt functions perform a similar check, but require a "non-trivial" linear form, which means that the b coefficient must be non-zero. For example, `lin(2,x)' returns [2, 0, x] and `lin(y,x)' returns [y, 0, x], but `linnt(2,x)' and `linnt(y,x)' are left unevaluated (in other words, these formulas are considered to be only "trivially" linear in x).

All four linearity-testing functions allow you to omit the second argument, in which case the input may be linear in any non-constant formula. Here, the a=0, b=1 case is also considered trivial, and only constant values for a and b are recognized. Thus, `lin(2 x y)' returns [0, 2, x y], `lin(2 - x y)' returns [2, -1, x y], and `lin(x y)' returns [0, 1, x y]. The linnt function would allow the first two cases but not the third. Also, neither lin nor linnt accept plain constants as linear in the one-argument case: `islin(2,x)' is true, but `islin(2)' is false.

The `istrue(a)' function returns 1 if a is a nonzero number or provably nonzero formula, or 0 if a is anything else. Calls to istrue can only be manipulated if m O mode is used to make sure they are not evaluated prematurely. (Note that declarations are used when deciding whether a formula is true; istrue returns 1 when dnonzero would return 1, and it returns 0 when dnonzero would return 0 or leave itself in symbolic form.)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11 Rewrite Rules

The a r (calc-rewrite) [rewrite] command makes substitutions in a formula according to a specified pattern or patterns known as rewrite rules. Whereas a b (calc-substitute) matches literally, so that substituting `sin(x)' with `cos(x)' matches only the sin function applied to the variable x, rewrite rules match general kinds of formulas; rewriting using the rule `sin(x) := cos(x)' matches sin of any argument and replaces it with cos of that same argument. The only significance of the name x is that the same name is used on both sides of the rule.

Rewrite rules rearrange formulas already in Calc's memory. See section 8.8.8 Syntax Tables, to read about syntax rules, which are similar to algebraic rewrite rules but operate when new algebraic entries are being parsed, converting strings of characters into Calc formulas.

12.11.1 Entering Rewrite Rules  
12.11.2 Basic Rewrite Rules  
12.11.3 Conditional Rewrite Rules  
12.11.4 Algebraic Properties of Rewrite Rules  
12.11.5 Other Features of Rewrite Rules  
12.11.6 Composing Patterns in Rewrite Rules  
12.11.7 Nested Formulas with Rewrite Rules  
12.11.8 Multi-Phase Rewrite Rules  
12.11.9 Selections with Rewrite Rules  
12.11.10 Matching Commands  
12.11.11 Automatic Rewrites  
12.11.12 Debugging Rewrites  
12.11.13 Examples of Rewrite Rules  

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.1 Entering Rewrite Rules

Rewrite rules normally use the "assignment" operator `old := new'. This operator is equivalent to the function call `assign(old, new)'. The assign function is undefined by itself in Calc, so an assignment formula such as a rewrite rule will be left alone by ordinary Calc commands. But certain commands, like the rewrite system, interpret assignments in special ways.

For example, the rule `sin(x)^2 := 1-cos(x)^2' says to replace every occurrence of the sine of something, squared, with one minus the square of the cosine of that same thing. All by itself as a formula on the stack it does nothing, but when given to the a r command it turns that command into a sine-squared-to-cosine-squared converter.

To specify a set of rules to be applied all at once, make a vector of rules.

When a r prompts you to enter the rewrite rules, you can answer in several ways:

  1. With a rule: f(x) := g(x) RET.
  2. With a vector of rules: [f1(x) := g1(x), f2(x) := g2(x)] RET. (You can omit the enclosing square brackets if you wish.)
  3. With the name of a variable that contains the rule or rules vector: myrules RET.
  4. With any formula except a rule, a vector, or a variable name; this will be interpreted as the old half of a rewrite rule, and you will be prompted a second time for the new half: f(x) RET g(x) RET.
  5. With a blank line, in which case the rule, rules vector, or variable will be taken from the top of the stack (and the formula to be rewritten will come from the second-to-top position).

If you enter the rules directly (as opposed to using rules stored in a variable), those rules will be put into the Trail so that you can retrieve them later. See section 7.3 Trail Commands.

It is most convenient to store rules you use often in a variable and invoke them by giving the variable name. The s e (calc-edit-variable) command is an easy way to create or edit a rule set stored in a variable. You may also wish to use s p (calc-permanent-variable) to save your rules permanently; see section 14.3 Other Operations on Variables.

Rewrite rules are compiled into a special internal form for faster matching. If you enter a rule set directly it must be recompiled every time. If you store the rules in a variable and refer to them through that variable, they will be compiled once and saved away along with the variable for later reference. This is another good reason to store your rules in a variable.

Calc also accepts an obsolete notation for rules, as vectors `[old, new]'. But because it is easily confused with a vector of two rules, the use of this notation is no longer recommended.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.2 Basic Rewrite Rules

To match a particular formula x with a particular rewrite rule `old := new', Calc compares the structure of x with the structure of old. Variables that appear in old are treated as meta-variables; the corresponding positions in x may contain any sub-formulas. For example, the pattern `f(x,y)' would match the expression `f(12, a+1)' with the meta-variable `x' corresponding to 12 and with `y' corresponding to `a+1'. However, this pattern would not match `f(12)' or `g(12, a+1)', since there is no assignment of the meta-variables that will make the pattern match these expressions. Notice that if the pattern is a single meta-variable, it will match any expression.

If a given meta-variable appears more than once in old, the corresponding sub-formulas of x must be identical. Thus the pattern `f(x,x)' would match `f(12, 12)' and `f(a+1, a+1)' but not `f(12, a+1)' or `f(a+b, b+a)'. (See section 12.11.3 Conditional Rewrite Rules, for a way to match the latter.)

Things other than variables must match exactly between the pattern and the target formula. To match a particular variable exactly, use the pseudo-function `quote(v)' in the pattern. For example, the pattern `x+quote(y)' matches `x+y', `2+y', or `sin(a)+y'.

The special variable names `e', `pi', `i', `phi', `gamma', `inf', `uinf', and `nan' always match literally. Thus the pattern `sin(d + e + f)' acts exactly like `sin(d + quote(e) + f)'.

If the old pattern is found to match a given formula, that formula is replaced by new, where any occurrences in new of meta-variables from the pattern are replaced with the sub-formulas that they matched. Thus, applying the rule `f(x,y) := g(y+x,x)' to `f(12, a+1)' would produce `g(a+13, 12)'.

The normal a r command applies rewrite rules over and over throughout the target formula until no further changes are possible (up to a limit of 100 times). Use C-u 1 a r to make only one change at a time.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.3 Conditional Rewrite Rules

A rewrite rule can also be conditional, written in the form `old := new :: cond'. (There is also the obsolete form `[old, new, cond]'.) If a cond part is present in the rule, this is an additional condition that must be satisfied before the rule is accepted. Once old has been successfully matched to the target expression, cond is evaluated (with all the meta-variables substituted for the values they matched) and simplified with a s (calc-simplify). If the result is a nonzero number or any other object known to be nonzero (see section 8.6 Declarations), the rule is accepted. If the result is zero or if it is a symbolic formula that is not known to be nonzero, the rule is rejected. See section 12.10 Logical Operations, for a number of functions that return 1 or 0 according to the results of various tests.

For example, the formula `n > 0' simplifies to 1 or 0 if n is replaced by a positive or nonpositive number, respectively (or if n has been declared to be positive or nonpositive). Thus, the rule `f(x,y) := g(y+x,x) :: x+y > 0' would apply to `f(0, 4)' but not to `f(-3, 2)' or `f(12, a+1)' (assuming no outstanding declarations for a). In the case of `f(-3, 2)', the condition can be shown not to be satisfied; in the case of `f(12, a+1)', the condition merely cannot be shown to be satisfied, but that is enough to reject the rule.

While Calc will use declarations to reason about variables in the formula being rewritten, declarations do not apply to meta-variables. For example, the rule `f(a) := g(a+1)' will match for any values of `a', such as complex numbers, vectors, or formulas, even if `a' has been declared to be real or scalar. If you want the meta-variable `a' to match only literal real numbers, use `f(a) := g(a+1) :: real(a)'. If you want `a' to match only reals and formulas which are provably real, use `dreal(a)' as the condition.

The `::' operator is a shorthand for the condition function; `old := new :: cond' is equivalent to the formula `condition(assign(old, new), cond)'.

If you have several conditions, you can use `... :: c1 :: c2 :: c3' or `... :: c1 && c2 && c3'. The two are entirely equivalent.

It is also possible to embed conditions inside the pattern: `f(x :: x>0, y) := g(y+x, x)'. This is purely a notational convenience, though; where a condition appears in a rule has no effect on when it is tested. The rewrite-rule compiler automatically decides when it is best to test each condition while a rule is being matched.

Certain conditions are handled as special cases by the rewrite rule system and are tested very efficiently: Where x is any meta-variable, these conditions are `integer(x)', `real(x)', `constant(x)', `negative(x)', `x >= y' where y is either a constant or another meta-variable and `>=' may be replaced by any of the six relational operators, and `x % a = b' where a and b are constants. Other conditions, like `x >= y+1' or `dreal(x)', will be less efficient to check since Calc must bring the whole evaluator and simplifier into play.

An interesting property of `::' is that neither of its arguments will be touched by Calc's default simplifications. This is important because conditions often are expressions that cannot safely be evaluated early. For example, the typeof function never remains in symbolic form; entering `typeof(a)' will put the number 100 (the type code for variables like `a') on the stack. But putting the condition `... :: typeof(a) = 6' on the stack is safe since `::' prevents the typeof from being evaluated until the condition is actually used by the rewrite system.

Since `::' protects its lefthand side, too, you can use a dummy condition to protect a rule that must itself not evaluate early. For example, it's not safe to put `a(f,x) := apply(f, [x])' on the stack because it will immediately evaluate to `a(f,x) := f(x)', where the meta-variable-ness of f on the righthand side has been lost. But `a(f,x) := apply(f, [x]) :: 1' is safe, and of course the condition `1' is always true (nonzero) so it has no effect on the functioning of the rule. (The rewrite compiler will ensure that it doesn't even impact the speed of matching the rule.)

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.4 Algebraic Properties of Rewrite Rules

The rewrite mechanism understands the algebraic properties of functions like `+' and `*'. In particular, pattern matching takes the associativity and commutativity of the following functions into account:

+ - *  = !=  && ||  and or xor  vint vunion vxor  gcd lcm  max min  beta

For example, the rewrite rule:

a x + b x  :=  (a + b) x

will match formulas of the form,

a x + b x,  x a + x b,  a x + x b,  x a + b x

Rewrites also understand the relationship between the `+' and `-' operators. The above rewrite rule will also match the formulas,

a x - b x,  x a - x b,  a x - x b,  x a - b x

by matching `b' in the pattern to `-b' from the formula.

Applied to a sum of many terms like `r + a x + s + b x + t', this pattern will check all pairs of terms for possible matches. The rewrite will take whichever suitable pair it discovers first.

In general, a pattern using an associative operator like `a + b' will try 2 n different ways to match a sum of n terms like `x + y + z - w'. First, `a' is matched against each of `x', `y', `z', and `-w' in turn, with `b' being matched to the remainders `y + z - w', `x + z - w', etc. If none of these succeed, then `b' is matched against each of the four terms with `a' matching the remainder. Half-and-half matches, like `(x + y) + (z - w)', are not tried.

Note that `*' is not commutative when applied to matrices, but rewrite rules pretend that it is. If you type m v to enable matrix mode (see section 8.4.6 Matrix and Scalar Modes), rewrite rules will match `*' literally, ignoring its usual commutativity property. (In the current implementation, the associativity also vanishes--it is as if the pattern had been enclosed in a plain marker; see below.) If you are applying rewrites to formulas with matrices, it's best to enable matrix mode first to prevent algebraically incorrect rewrites from occurring.

The pattern `-x' will actually match any expression. For example, the rule

f(-x)  :=  -f(x)

will rewrite `f(a)' to `-f(-a)'. To avoid this, either use a plain marker as described below, or add a `negative(x)' condition. The negative function is true if its argument "looks" negative, for example, because it is a negative number or because it is a formula like `-x'. The new rule using this condition is:

f(x)  :=  -f(-x)  :: negative(x)    or, equivalently,
f(-x)  :=  -f(x)  :: negative(-x)

In the same way, the pattern `x - y' will match the sum `a + b' by matching `y' to `-b'.

The pattern `a b' will also match the formula `x/y' if `y' is a number. Thus the rule `a x + b x := (a+b) x' will also convert `a x + x / 2' to `(a + 0.5) x' (or `(a + 1:2) x', depending on the current fraction mode).

Calc will not take other liberties with `*', `/', and `^'. For example, the pattern `f(a b)' will not match `f(x^2)', and `f(a + b)' will not match `f(2 x)', even though conceivably these patterns could match with `a = b = x'. Nor will `f(a b)' match `f(x / y)' if `y' is not a constant, even though it could be considered to match with `a = x' and `b = 1/y'. The reasons are partly for efficiency, and partly because while few mathematical operations are substantively different for addition and subtraction, often it is preferable to treat the cases of multiplication, division, and integer powers separately.

Even more subtle is the rule set

[ f(a) + f(b) := f(a + b),  -f(a) := f(-a) ]

attempting to match `f(x) - f(y)'. You might think that Calc will view this subtraction as `f(x) + (-f(y))' and then apply the above two rules in turn, but actually this will not work because Calc only does this when considering rules for `+' (like the first rule in this set). So it will see first that `f(x) + (-f(y))' does not match `f(a) + f(b)' for any assignments of the meta-variables, and then it will see that `f(x) - f(y)' does not match `-f(a)' for any assignment of `a'. Because Calc tries only one rule at a time, it will not be able to rewrite `f(x) - f(y)' with this rule set. An explicit `f(a) - f(b)' rule will have to be added.

Another thing patterns will not do is break up complex numbers. The pattern `myconj(a + b i) := a - b i' will work for formulas involving the special constant `i' (such as `3 - 4 i'), but it will not match actual complex numbers like `(3, -4)'. A version of the above rule for complex numbers would be

myconj(a)  :=  re(a) - im(a) (0,1)  :: im(a) != 0

(Because the re and im functions understand the properties of the special constant `i', this rule will also work for `3 - 4 i'. In fact, this particular rule would probably be better without the `im(a) != 0' condition, since if `im(a) = 0' the righthand side of the rule will still give the correct answer for the conjugate of a real number.)

It is also possible to specify optional arguments in patterns. The rule

opt(a) x + opt(b) (x^opt(c) + opt(d))  :=  f(a, b, c, d)

will match the formula

5 (x^2 - 4) + 3 x

in a fairly straightforward manner, but it will also match reduced formulas like

x + x^2,    2(x + 1) - x,    x + x

producing, respectively,

f(1, 1, 2, 0),   f(-1, 2, 1, 1),   f(1, 1, 1, 0)

(The latter two formulas can be entered only if default simplifications have been turned off with m O.)

The default value for a term of a sum is zero. The default value for a part of a product, for a power, or for the denominator of a quotient, is one. Also, `-x' matches the pattern `opt(a) b' with `a = -1'.

In particular, the distributive-law rule can be refined to

opt(a) x + opt(b) x  :=  (a + b) x

so that it will convert, e.g., `a x - x', to `(a - 1) x'.

The pattern `opt(a) + opt(b) x' matches almost any formulas which are linear in `x'. You can also use the lin and islin functions with rewrite conditions to test for this; see section 12.10 Logical Operations. These functions are not as convenient to use in rewrite rules, but they recognize more kinds of formulas as linear: `x/z' is considered linear with b = 1/z by lin, but it will not match the above pattern because that pattern calls for a multiplication, not a division.

As another example, the obvious rule to replace `sin(x)^2 + cos(x)^2' by 1,

sin(x)^2 + cos(x)^2  :=  1

misses many cases because the sine and cosine may both be multiplied by an equal factor. Here's a more successful rule:

opt(a) sin(x)^2 + opt(a) cos(x)^2  :=  a

Note that this rule will not match `sin(x)^2 + 6 cos(x)^2' because one a would have "matched" 1 while the other matched 6.

Calc automatically converts a rule like

f(x-1, x)  :=  g(x)

into the form

f(temp, x)  :=  g(x)  :: temp = x-1

(where temp stands for a new, invented meta-variable that doesn't actually have a name). This modified rule will successfully match `f(6, 7)', binding `temp' and `x' to 6 and 7, respectively, then verifying that they differ by one even though `6' does not superficially look like `x-1'.

However, Calc does not solve equations to interpret a rule. The following rule,

f(x-1, x+1)  :=  g(x)

will not work. That is, it will match `f(a - 1 + b, a + 1 + b)' but not `f(6, 8)'. Calc always interprets at least one occurrence of a variable by literal matching. If the variable appears "isolated" then Calc is smart enough to use it for literal matching. But in this last example, Calc is forced to rewrite the rule to `f(x-1, temp) := g(x) :: temp = x+1' where the `x-1' term must correspond to an actual "something-minus-one" in the target formula.

A successful way to write this would be `f(x, x+2) := g(x+1)'. You could make this resemble the original form more closely by using let notation, which is described in the next section:

f(xm1, x+1)  :=  g(x)  :: let(x := xm1+1)

Calc does this rewriting or "conditionalizing" for any sub-pattern which involves only the functions in the following list, operating only on constants and meta-variables which have already been matched elsewhere in the pattern. When matching a function call, Calc is careful to match arguments which are plain variables before arguments which are calls to any of the functions below, so that a pattern like `f(x-1, x)' can be conditionalized even though the isolated `x' comes after the `x-1'.

+ - * / \ % ^  abs sign  round rounde roundu trunc floor ceil
max min  re im conj arg

You can suppress all of the special treatments described in this section by surrounding a function call with a plain marker. This marker causes the function call which is its argument to be matched literally, without regard to commutativity, associativity, negation, or conditionalization. When you use plain, the "deep structure" of the formula being matched can show through. For example,

plain(a - a b)  :=  f(a, b)

will match only literal subtractions. However, the plain marker does not affect its arguments' arguments. In this case, commutativity and associativity is still considered while matching the `a b' sub-pattern, so the whole pattern will match `x - y x' as well as `x - x y'. We could go still further and use

plain(a - plain(a b))  :=  f(a, b)

which would do a completely strict match for the pattern.

By contrast, the quote marker means that not only the function name but also the arguments must be literally the same. The above pattern will match `x - x y' but

quote(a - a b)  :=  f(a, b)

will match only the single formula `a - a b'. Also,

quote(a - quote(a b))  :=  f(a, b)

will match only `a - quote(a b)'---probably not the desired effect!

A certain amount of algebra is also done when substituting the meta-variables on the righthand side of a rule. For example, in the rule

a + f(b)  :=  f(a + b)

matching `f(x) - y' would produce `f((-y) + x)' if taken literally, but the rewrite mechanism will simplify the righthand side to `f(x - y)' automatically. (Of course, the default simplifications would do this anyway, so this special simplification is only noticeable if you have turned the default simplifications off.) This rewriting is done only when a meta-variable expands to a "negative-looking" expression. If this simplification is not desirable, you can use a plain marker on the righthand side:

a + f(b)  :=  f(plain(a + b))

In this example, we are still allowing the pattern-matcher to use all the algebra it can muster, but the righthand side will always simplify to a literal addition like `f((-y) + x)'.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.5 Other Features of Rewrite Rules

Certain "function names" serve as markers in rewrite rules. Here is a complete list of these markers. First are listed the markers that work inside a pattern; then come the markers that work in the righthand side of a rule.

One kind of marker, `import(x)', takes the place of a whole rule. Here x is the name of a variable containing another rule set; those rules are "spliced into" the rule set that imports them. For example, if `[f(a+b) := f(a) + f(b), f(a b) := a f(b) :: real(a)]' is stored in variable `linearF', then the rule set `[f(0) := 0, import(linearF)]' will apply all three rules. It is possible to modify the imported rules slightly: `import(x, v1, x1, v2, x2, ...)' imports the rule set x with all occurrences of v1, as either a variable name or a function name, replaced with x1 and so on. (If v1 is used as a function name, then x1 must be either a function name itself or a `< >' nameless function; see section 11.8.1 Specifying Operators.) For example, `[g(0) := 0, import(linearF, f, g)]' applies the linearity rules to the function `g' instead of `f'. Imports can be nested, but the import-with-renaming feature may fail to rename sub-imports properly.

The special functions allowed in patterns are:

This pattern matches exactly x; variable names in x are not interpreted as meta-variables. The only flexibility is that numbers are compared for numeric equality, so that the pattern `f(quote(12))' will match both `f(12)' and `f(12.0)'. (Numbers are always treated this way by the rewrite mechanism: The rule `f(x,x) := g(x)' will match `f(12, 12.0)'. The rewrite may produce either `g(12)' or `g(12.0)' as a result in this case.)

Here x must be a function call `f(x1,x2,...)'. This pattern matches a call to function f with the specified argument patterns. No special knowledge of the properties of the function f is used in this case; `+' is not commutative or associative. Unlike quote, the arguments `x1,x2,...' are treated as patterns. If you wish them to be treated "plainly" as well, you must enclose them with more plain markers: `plain(plain(-a) + plain(b c))'.

Here x must be a variable name. This must appear as an argument to a function or an element of a vector; it specifies that the argument or element is optional. As an argument to `+', `-', `*', `&&', or `||', or as the second argument to `/' or `^', the value def may be omitted. The pattern `x + opt(y)' matches a sum by binding one summand to x and the other to y, and it matches anything else by binding the whole expression to x and zero to y. The other operators above work similarly.

For general miscellanous functions, the default value def must be specified. Optional arguments are dropped starting with the rightmost one during matching. For example, the pattern `f(opt(a,0), b, opt(c,b))' will match `f(b)', `f(a,b)', or `f(a,b,c)'. Default values of zero and b are supplied in this example for the omitted arguments. Note that the literal variable b will be the default in the latter case, not the value that matched the meta-variable b. In other words, the default def is effectively quoted.

This matches the pattern x, with the attached condition c. It is the same as `x :: c'.

This matches anything that matches both pattern x and pattern y. It is the same as `x &&& y'. see section 12.11.6 Composing Patterns in Rewrite Rules.

This matches anything that matches either pattern x or pattern y. It is the same as `x ||| y'.

This matches anything that does not match pattern x. It is the same as `!!! x'.

This matches any vector of one or more elements. The first element is matched to h; a vector of the remaining elements is matched to t. Note that vectors of fixed length can also be matched as actual vectors: The rule `cons(a,cons(b,[])) := cons(a+b,[])' is equivalent to the rule `[a,b] := [a+b]'.

This is like cons, except that the last element is matched to h, with the remaining elements matched to t.

This matches any function call. The name of the function, in the form of a variable, is matched to f. The arguments of the function, as a vector of zero or more objects, are matched to `args'. Constants, variables, and vectors do not match an apply pattern. For example, `apply(f,x)' matches any function call, `apply(quote(f),x)' matches any call to the function `f', `apply(f,[a,b])' matches any function call with exactly two arguments, and `apply(quote(f), cons(a,cons(b,x)))' matches any call to the function `f' with two or more arguments. Another way to implement the latter, if the rest of the rule does not need to refer to the first two arguments of `f' by name, would be `apply(quote(f), x :: vlen(x) >= 2)'. Here's a more interesting sample use of apply:

apply(f,[x+n])  :=  n + apply(f,[x])
   :: in(f, [floor,ceil,round,trunc]) :: integer(n)

Note, however, that this will be slower to match than a rule set with four separate rules. The reason is that Calc sorts the rules of a rule set according to top-level function name; if the top-level function is apply, Calc must try the rule for every single formula and sub-formula. If the top-level function in the pattern is, say, floor, then Calc invokes the rule only for sub-formulas which are calls to floor.

Formulas normally written with operators like + are still considered function calls: apply(f,x) matches `a+b' with `f = add', `x = [a,b]'.

You must use apply for meta-variables with function names on both sides of a rewrite rule: `apply(f, [x]) := f(x+1)' is not correct, because it rewrites `spam(6)' into `f(7)'. The righthand side should be `apply(f, [x+1])'. Also note that you will have to use no-simplify (m O) mode when entering this rule so that the apply isn't evaluated immediately to get the new rule `f(x) := f(x+1)'. Or, use s e to enter the rule without going through the stack, or enter the rule as `apply(f, [x]) := apply(f, [x+1]) :: 1'. See section 12.11.3 Conditional Rewrite Rules.

This is used for applying rules to formulas with selections; see section 12.11.9 Selections with Rewrite Rules.

Special functions for the righthand sides of rules are:

The notation `quote(x)' is changed to `x' when the righthand side is used. As far as the rewrite rule is concerned, quote is invisible. However, quote has the special property in Calc that its argument is not evaluated. Thus, while it will not work to put the rule `t(a) := typeof(a)' on the stack because `typeof(a)' is evaluated immediately to produce `t(a) := 100', you can use quote to protect the righthand side: `t(a) := quote(typeof(a))'. (See section 12.11.3 Conditional Rewrite Rules, for another trick for protecting rules from evaluation.)

Special properties of and simplifications for the function call x are not used. One interesting case where plain is useful is the rule, `q(x) := quote(x)', trying to expand a shorthand notation for the quote function. This rule will not work as shown; instead of replacing `q(foo)' with `quote(foo)', it will replace it with `foo'! The correct rule would be `q(x) := plain(quote(x))'.

Where t is a vector, this is converted into an expanded vector during rewrite processing. Note that cons is a regular Calc function which normally does this anyway; the only way cons is treated specially by rewrites is that cons on the righthand side of a rule will be evaluated even if default simplifications have been turned off.

Analogous to cons except putting h at the end of the vector t.

Where f is a variable and args is a vector, this is converted to a function call. Once again, note that apply is also a regular Calc function.

The formula x is handled in the usual way, then the default simplifications are applied to it even if they have been turned off normally. This allows you to treat any function similarly to the way cons and apply are always treated. However, there is a slight difference: `cons(2+3, [])' with default simplifications off will be converted to `[2+3]', whereas `eval(cons(2+3, []))' will be converted to `[5]'.

The formula x has meta-variables substituted in the usual way, then algebraically simplified as if by the a s command.

The formula x has meta-variables substituted in the normal way, then "extendedly" simplified as if by the a e command.

See section 12.11.9 Selections with Rewrite Rules.

There are also some special functions you can use in conditions.

`let(v := x)'
The expression x is evaluated with meta-variables substituted. The a s command's simplifications are not applied by default, but x can include calls to evalsimp or evalextsimp as described above to invoke higher levels of simplification. The result of x is then bound to the meta-variable v. As usual, if this meta-variable has already been matched to something else the two values must be equal; if the meta-variable is new then it is bound to the result of the expression. This variable can then appear in later conditions, and on the righthand side of the rule. In fact, v may be any pattern in which case the result of evaluating x is matched to that pattern, binding any meta-variables that appear in that pattern. Note that let can only appear by itself as a condition, or as one term of an `&&' which is a whole condition: It cannot be inside an `||' term or otherwise buried.

The alternate, equivalent form `let(v, x)' is also recognized. Note that the use of `:=' by let, while still being assignment-like in character, is unrelated to the use of `:=' in the main part of a rewrite rule.

As an example, `f(a) := g(ia) :: let(ia := 1/a) :: constant(ia)' replaces `f(a)' with `g' of the inverse of `a', if that inverse exists and is constant. For example, if `a' is a singular matrix the operation `1/a' is left unsimplified and `constant(ia)' fails, but if `a' is an invertible matrix then the rule succeeds. Without let there would be no way to express this rule that didn't have to invert the matrix twice. Note that, because the meta-variable `ia' is otherwise unbound in this rule, the let condition itself always "succeeds" because no matter what `1/a' evaluates to, it can successfully be bound to ia.

Here's another example, for integrating cosines of linear terms: `myint(cos(y),x) := sin(y)/b :: let([a,b,x] := lin(y,x))'. The lin function returns a 3-vector if its argument is linear, or leaves itself unevaluated if not. But an unevaluated lin call will not match the 3-vector on the lefthand side of the let, so this let both verifies that y is linear, and binds the coefficients a and b for use elsewhere in the rule. (It would have been possible to use `sin(a x + b)/b' for the righthand side instead, but using `sin(y)/b' avoids gratuitous rearrangement of the argument of the sine.)

Similarly, here is a rule that implements an inverse-erf function. It uses root to search for a solution. If root succeeds, it will return a vector of two numbers where the first number is the desired solution. If no solution is found, root remains in symbolic form. So we use let to check that the result was indeed a vector.

ierf(x)  :=  y  :: let([y,z] := root(erf(a) = x, a, .5))

The meta-variable v, which must already have been matched to something elsewhere in the rule, is compared against pattern p. Since matches is a standard Calc function, it can appear anywhere in a condition. But if it appears alone or as a term of a top-level `&&', then you get the special extra feature that meta-variables which are bound to things inside p can be used elsewhere in the surrounding rewrite rule.

The only real difference between `let(p := v)' and `matches(v, p)' is that the former evaluates `v' using the default simplifications, while the latter does not.

This is actually a variable, not a function. If remember appears as a condition in a rule, then when that rule succeeds the original expression and rewritten expression are added to the front of the rule set that contained the rule. If the rule set was not stored in a variable, remember is ignored. The lefthand side is enclosed in quote in the added rule if it contains any variables.

For example, the rule `f(n) := n f(n-1) :: remember' applied to `f(7)' will add the rule `f(7) := 7 f(6)' to the front of the rule set. The rule set EvalRules works slightly differently: There, the evaluation of `f(6)' will complete before the result is added to the rule set, in this case as `f(7) := 5040'. Thus remember is most useful inside EvalRules.

It is up to you to ensure that the optimization performed by remember is safe. For example, the rule `foo(n) := n :: evalv(eatfoo) > 0 :: remember' is a bad idea (evalv is the function equivalent of the = command); if the variable eatfoo ever contains 1, rules like `foo(7) := 7' will be added to the rule set and will continue to operate even if eatfoo is later changed to 0.

Remember the match as described above, but only if condition c is true. For example, `remember(n % 4 = 0)' in the above factorial rule remembers only every fourth result. Note that `remember(1)' is equivalent to `remember', and `remember(0)' has no effect.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.6 Composing Patterns in Rewrite Rules

There are three operators, `&&&', `|||', and `!!!', that combine rewrite patterns to make larger patterns. The combinations are "and," "or," and "not," respectively, and these operators are the pattern equivalents of `&&', `||' and `!' (which operate on zero-or-nonzero logical values).

Note that `&&&', `|||', and `!!!' are left in symbolic form by all regular Calc features; they have special meaning only in the context of rewrite rule patterns.

The pattern `p1 &&& p2' matches anything that matches both p1 and p2. One especially useful case is when one of p1 or p2 is a meta-variable. For example, here is a rule that operates on error forms:

f(x &&& a +/- b, x)  :=  g(x)

This does the same thing, but is arguably simpler than, the rule

f(a +/- b, a +/- b)  :=  g(a +/- b)

Here's another interesting example:

ends(cons(a, x) &&& rcons(y, b))  :=  [a, b]

which effectively clips out the middle of a vector leaving just the first and last elements. This rule will change a one-element vector `[a]' to `[a, a]'. The similar rule

ends(cons(a, rcons(y, b)))  :=  [a, b]

would do the same thing except that it would fail to match a one-element vector.

The pattern `p1 ||| p2' matches anything that matches either p1 or p2. Calc first tries matching against p1; if that fails, it goes on to try p2.

A simple example of `|||' is

curve(inf ||| -inf)  :=  0

which converts both `curve(inf)' and `curve(-inf)' to zero.

Here is a larger example:

log(a, b) ||| (ln(a) :: let(b := e))  :=  mylog(a, b)

This matches both generalized and natural logarithms in a single rule. Note that the `::' term must be enclosed in parentheses because that operator has lower precedence than `|||' or `:='.

(In practice this rule would probably include a third alternative, omitted here for brevity, to take care of log10.)

While Calc generally treats interior conditions exactly the same as conditions on the outside of a rule, it does guarantee that if all the variables in the condition are special names like e, or already bound in the pattern to which the condition is attached (say, if `a' had appeared in this condition), then Calc will process this condition right after matching the pattern to the left of the `::'. Thus, we know that `b' will be bound to `e' only if the ln branch of the `|||' was taken.

Note that this rule was careful to bind the same set of meta-variables on both sides of the `|||'. Calc does not check this, but if you bind a certain meta-variable only in one branch and then use that meta-variable elsewhere in the rule, results are unpredictable:

f(a,b) ||| g(b)  :=  h(a,b)

Here if the pattern matches `g(17)', Calc makes no promises about the value that will be substituted for `a' on the righthand side.

The pattern `!!! pat' matches anything that does not match pat. Any meta-variables that are bound while matching pat remain unbound outside of pat.

For example,

f(x &&& !!! a +/- b, !!![])  :=  g(x)

converts f whose first argument is anything except an error form, and whose second argument is not the empty vector, into a similar call to g (but without the second argument).

If we know that the second argument will be a vector (empty or not), then an equivalent rule would be:

f(x, y)  :=  g(x)  :: typeof(x) != 7 :: vlen(y) > 0

where of course 7 is the typeof code for error forms. Another final condition, that works for any kind of `y', would be `!istrue(y == [])'. (The istrue function returns an explicit 0 if its argument was left in symbolic form; plain `!(y == [])' or `y != []' would not work to replace `!!![]' since these would be left unsimplified, and thus cause the rule to fail, if `y' was something like a variable name.)

It is possible for a `!!!' to refer to meta-variables bound elsewhere in the pattern. For example,

f(a, !!!a)  :=  g(a)

matches any call to f with different arguments, changing this to g with only the first argument.

If a function call is to be matched and one of the argument patterns contains a `!!!' somewhere inside it, that argument will be matched last. Thus

f(!!!a, a)  :=  g(a)

will be careful to bind `a' to the second argument of f before testing the first argument. If Calc had tried to match the first argument of f first, the results would have been disasterous: Since a was unbound so far, the pattern `a' would have matched anything at all, and the pattern `!!!a' therefore would not have matched anything at all!

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.7 Nested Formulas with Rewrite Rules

When a r (calc-rewrite) is used, it takes an expression from the top of the stack and attempts to match any of the specified rules to any part of the expression, starting with the whole expression and then, if that fails, trying deeper and deeper sub-expressions. For each part of the expression, the rules are tried in the order they appear in the rules vector. The first rule to match the first sub-expression wins; it replaces the matched sub-expression according to the new part of the rule.

Often, the rule set will match and change the formula several times. The top-level formula is first matched and substituted repeatedly until it no longer matches the pattern; then, sub-formulas are tried, and so on. Once every part of the formula has gotten its chance, the rewrite mechanism starts over again with the top-level formula (in case a substitution of one of its arguments has caused it again to match). This continues until no further matches can be made anywhere in the formula.

It is possible for a rule set to get into an infinite loop. The most obvious case, replacing a formula with itself, is not a problem because a rule is not considered to "succeed" unless the righthand side actually comes out to something different than the original formula or sub-formula that was matched. But if you accidentally had both `ln(a b) := ln(a) + ln(b)' and the reverse `ln(a) + ln(b) := ln(a b)' in your rule set, Calc would run forever switching a formula back and forth between the two forms.

To avoid disaster, Calc normally stops after 100 changes have been made to the formula. This will be enough for most multiple rewrites, but it will keep an endless loop of rewrites from locking up the computer forever. (On most systems, you can also type C-g to halt any Emacs command prematurely.)

To change this limit, give a positive numeric prefix argument. In particular, M-1 a r applies only one rewrite at a time, useful when you are first testing your rule (or just if repeated rewriting is not what is called for by your application).

You can also put a "function call" `iterations(n)' in place of a rule anywhere in your rules vector (but usually at the top). Then, n will be used instead of 100 as the default number of iterations for this rule set. You can use `iterations(inf)' if you want no iteration limit by default. A prefix argument will override the iterations limit in the rule set.

[ iterations(1),
  f(x) := f(x+1) ]

More precisely, the limit controls the number of "iterations," where each iteration is a successful matching of a rule pattern whose righthand side, after substituting meta-variables and applying the default simplifications, is different from the original sub-formula that was matched.

A prefix argument of zero sets the limit to infinity. Use with caution!

Given a negative numeric prefix argument, a r will match and substitute the top-level expression up to that many times, but will not attempt to match the rules to any sub-expressions.

In a formula, rewrite(expr, rules, n) does a rewriting operation. Here expr is the expression being rewritten, rules is the rule, vector of rules, or variable containing the rules, and n is the optional iteration limit, which may be a positive integer, a negative integer, or `inf' or `-inf'. If n is omitted the iterations value from the rule set is used; if both are omitted, 100 is used.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.8 Multi-Phase Rewrite Rules

It is possible to separate a rewrite rule set into several phases. During each phase, certain rules will be enabled while certain others will be disabled. A phase schedule controls the order in which phases occur during the rewriting process.

If a call to the marker function phase appears in the rules vector in place of a rule, all rules following that point will be members of the phase(s) identified in the arguments to phase. Phases are given integer numbers. The markers `phase()' and `phase(all)' both mean the following rules belong to all phases; this is the default at the start of the rule set.

If you do not explicitly schedule the phases, Calc sorts all phase numbers that appear in the rule set and executes the phases in ascending order. For example, the rule set

[ f0(x) := g0(x),
  f1(x) := g1(x),
  f2(x) := g2(x),
  f3(x) := g3(x),
  f4(x) := g4(x) ]

has three phases, 1 through 3. Phase 1 consists of the f0, f1, and f4 rules (in that order). Phase 2 consists of f0, f2, and f4. Phase 3 consists of f0 and f3.

When Calc rewrites a formula using this rule set, it first rewrites the formula using only the phase 1 rules until no further changes are possible. Then it switches to the phase 2 rule set and continues until no further changes occur, then finally rewrites with phase 3. When no more phase 3 rules apply, rewriting finishes. (This is assuming a r with a large enough prefix argument to allow the rewriting to run to completion; the sequence just described stops early if the number of iterations specified in the prefix argument, 100 by default, is reached.)

During each phase, Calc descends through the nested levels of the formula as described previously. (See section 12.11.7 Nested Formulas with Rewrite Rules.) Rewriting starts at the top of the formula, then works its way down to the parts, then goes back to the top and works down again. The phase 2 rules do not begin until no phase 1 rules apply anywhere in the formula.

A schedule marker appearing in the rule set (anywhere, but conventionally at the top) changes the default schedule of phases. In the simplest case, schedule has a sequence of phase numbers for arguments; each phase number is invoked in turn until the arguments to schedule are exhausted. Thus adding `schedule(3,2,1)' at the top of the above rule set would reverse the order of the phases; `schedule(1,2,3)' would have no effect since this is the default schedule; and `schedule(1,2,1,3)' would give phase 1 a second chance after phase 2 has completed, before moving on to phase 3.

Any argument to schedule can instead be a vector of phase numbers (or even of sub-vectors). Then the sub-sequence of phases described by the vector are tried repeatedly until no change occurs in any phase in the sequence. For example, `schedule([1, 2], 3)' tries phase 1, then phase 2, then, if either phase made any changes to the formula, repeats these two phases until they can make no further progress. Finally, it goes on to phase 3 for finishing touches.

Also, items in schedule can be variable names as well as numbers. A variable name is interpreted as the name of a function to call on the whole formula. For example, `schedule(1, simplify)' says to apply the phase-1 rules (presumably, all of them), then to call simplify which is the function name equivalent of a s. Likewise, `schedule([1, simplify])' says to alternate between phase 1 and a s until no further changes occur.

Phases can be used purely to improve efficiency; if it is known that a certain group of rules will apply only at the beginning of rewriting, and a certain other group will apply only at the end, then rewriting will be faster if these groups are identified as separate phases. Once the phase 1 rules are done, Calc can put them aside and no longer spend any time on them while it works on phase 2.

There are also some problems that can only be solved with several rewrite phases. For a real-world example of a multi-phase rule set, examine the set FitRules, which is used by the curve-fitting command to convert a model expression to linear form. See section 12.8.5 Curve Fitting Details. This set is divided into four phases. The first phase rewrites certain kinds of expressions to be more easily linearizable, but less computationally efficient. After the linear components have been picked out, the final phase includes the opposite rewrites to put each component back into an efficient form. If both sets of rules were included in one big phase, Calc could get into an infinite loop going back and forth between the two forms.

Elsewhere in FitRules, the components are first isolated, then recombined where possible to reduce the complexity of the linear fit, then finally packaged one component at a time into vectors. If the packaging rules were allowed to begin before the recombining rules were finished, some components might be put away into vectors before they had a chance to recombine. By putting these rules in two separate phases, this problem is neatly avoided.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.9 Selections with Rewrite Rules

If a sub-formula of the current formula is selected (as by j s; see section 12.1 Selecting Sub-Formulas), the a r (calc-rewrite) command applies only to that sub-formula. Together with a negative prefix argument, you can use this fact to apply a rewrite to one specific part of a formula without affecting any other parts.

The j r (calc-rewrite-selection) command allows more sophisticated operations on selections. This command prompts for the rules in the same way as a r, but it then applies those rules to the whole formula in question even though a sub-formula of it has been selected. However, the selected sub-formula will first have been surrounded by a `select( )' function call. (Calc's evaluator does not understand the function name select; this is only a tag used by the j r command.)

For example, suppose the formula on the stack is `2 (a + b)^2' and the sub-formula `a + b' is selected. This formula will be rewritten to `2 select(a + b)^2' and then the rewrite rules will be applied in the usual way. The rewrite rules can include references to select to tell where in the pattern the selected sub-formula should appear.

If there is still exactly one `select( )' function call in the formula after rewriting is done, it indicates which part of the formula should be selected afterwards. Otherwise, the formula will be unselected.

You can make j r act much like a r by enclosing both parts of the rewrite rule with `select()'. However, j r allows you to use the current selection in more flexible ways. Suppose you wished to make a rule which removed the exponent from the selected term; the rule `select(a)^x := select(a)' would work. In the above example, it would rewrite `2 select(a + b)^2' to `2 select(a + b)'. This would then be returned to the stack as `2 (a + b)' with the `a + b' selected.

The j r command uses one iteration by default, unlike a r which defaults to 100 iterations. A numeric prefix argument affects j r in the same way as a r. See section 12.11.7 Nested Formulas with Rewrite Rules.

As with other selection commands, j r operates on the stack entry that contains the cursor. (If the cursor is on the top-of-stack `.' marker, it works as if the cursor were on the formula at stack level 1.)

If you don't specify a set of rules, the rules are taken from the top of the stack, just as with a r. In this case, the cursor must indicate stack entry 2 or above as the formula to be rewritten (otherwise the same formula would be used as both the target and the rewrite rules).

If the indicated formula has no selection, the cursor position within the formula temporarily selects a sub-formula for the purposes of this command. If the cursor is not on any sub-formula (e.g., it is in the line-number area to the left of the formula), the `select( )' markers are ignored by the rewrite mechanism and the rules are allowed to apply anywhere in the formula.

As a special feature, the normal a r command also ignores `select( )' calls in rewrite rules. For example, if you used the above rule `select(a)^x := select(a)' with a r, it would apply the rule as if it were `a^x := a'. Thus, you can write general purpose rules with `select( )' hints inside them so that they will "do the right thing" in both a r and j r, both with and without selections.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.10 Matching Commands

The a m (calc-match) [match] function takes a vector of formulas and a rewrite-rule-style pattern, and produces a vector of all formulas which match the pattern. The command prompts you to enter the pattern; as for a r, you can enter a single pattern (i.e., a formula with meta-variables), or a vector of patterns, or a variable which contains patterns, or you can give a blank response in which case the patterns are taken from the top of the stack. The pattern set will be compiled once and saved if it is stored in a variable. If there are several patterns in the set, vector elements are kept if they match any of the patterns.

For example, `match(a+b, [x, x+y, x-y, 7, x+y+z])' will return `[x+y, x-y, x+y+z]'.

The import mechanism is not available for pattern sets.

The a m command can also be used to extract all vector elements which satisfy any condition: The pattern `x :: x>0' will select all the positive vector elements.

With the Inverse flag [matchnot], this command extracts all vector elements which do not match the given pattern.

There is also a function `matches(x, p)' which evaluates to 1 if expression x matches pattern p, or to 0 otherwise. This is sometimes useful for including into the conditional clauses of other rewrite rules.

The function vmatches is just like matches, except that if the match succeeds it returns a vector of assignments to the meta-variables instead of the number 1. For example, `vmatches(f(1,2), f(a,b))' returns `[a := 1, b := 2]'. If the match fails, the function returns the number 0.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.11 Automatic Rewrites

It is possible to get Calc to apply a set of rewrite rules on all results, effectively adding to the built-in set of default simplifications. To do this, simply store your rule set in the variable EvalRules. There is a convenient s E command for editing EvalRules; see section 14.3 Other Operations on Variables.

For example, suppose you want `sin(a + b)' to be expanded out to `sin(b) cos(a) + cos(b) sin(a)' wherever it appears, and similarly for `cos(a + b)'. The corresponding rewrite rule set would be,

[ sin(a + b)  :=  cos(a) sin(b) + sin(a) cos(b),
  cos(a + b)  :=  cos(a) cos(b) - sin(a) sin(b) ]

To apply these manually, you could put them in a variable called trigexp and then use a r trigexp every time you wanted to expand trig functions. But if instead you store them in the variable EvalRules, they will automatically be applied to all sines and cosines of sums. Then, with `2 x' and `45' on the stack, typing + S will (assuming degrees mode) result in `0.7071 sin(2 x) + 0.7071 cos(2 x)' automatically.

As each level of a formula is evaluated, the rules from EvalRules are applied before the default simplifications. Rewriting continues until no further EvalRules apply. Note that this is different from the usual order of application of rewrite rules: EvalRules works from the bottom up, simplifying the arguments to a function before the function itself, while a r applies rules from the top down.

Because the EvalRules are tried first, you can use them to override the normal behavior of any built-in Calc function.

It is important not to write a rule that will get into an infinite loop. For example, the rule set `[f(0) := 1, f(n) := n f(n-1)]' appears to be a good definition of a factorial function, but it is unsafe. Imagine what happens if `f(2.5)' is simplified. Calc will continue to subtract 1 from this argument forever without reaching zero. A safer second rule would be `f(n) := n f(n-1) :: n>0'. Another dangerous rule is `g(x, y) := g(y, x)'. Rewriting `g(2, 4)', this would bounce back and forth between that and `g(4, 2)' forever. If an infinite loop in EvalRules occurs, Emacs will eventually stop with a "Computation got stuck or ran too long" message.

Another subtle difference between EvalRules and regular rewrites concerns rules that rewrite a formula into an identical formula. For example, `f(n) := f(floor(n))' "fails to match" when n is already an integer. But in EvalRules this case is detected only if the righthand side literally becomes the original formula before any further simplification. This means that `f(n) := f(floor(n))' will get into an infinite loop if it occurs in EvalRules. Calc will replace `f(6)' with `f(floor(6))', which is different from `f(6)', so it will consider the rule to have matched and will continue simplifying that formula; first the argument is simplified to get `f(6)', then the rule matches again to get `f(floor(6))' again, ad infinitum. A much safer rule would check its argument first, say, with `f(n) := f(floor(n)) :: !dint(n)'.

(What really happens is that the rewrite mechanism substitutes the meta-variables in the righthand side of a rule, compares to see if the result is the same as the original formula and fails if so, then uses the default simplifications to simplify the result and compares again (and again fails if the formula has simplified back to its original form). The only special wrinkle for the EvalRules is that the same rules will come back into play when the default simplifications are used. What Calc wants to do is build `f(floor(6))', see that this is different from the original formula, simplify to `f(6)', see that this is the same as the original formula, and thus halt the rewriting. But while simplifying, `f(6)' will again trigger the same EvalRules rule and Calc will get into a loop inside the rewrite mechanism itself.)

The phase, schedule, and iterations markers do not work in EvalRules. If the rule set is divided into phases, only the phase 1 rules are applied, and the schedule is ignored. The rules are always repeated as many times as possible.

The EvalRules are applied to all function calls in a formula, but not to numbers (and other number-like objects like error forms), nor to vectors or individual variable names. (Though they will apply to components of vectors and error forms when appropriate.) You might try to make a variable phihat which automatically expands to its definition without the need to press = by writing the rule `quote(phihat) := (1-sqrt(5))/2', but unfortunately this rule will not work as part of EvalRules.

Finally, another limitation is that Calc sometimes calls its built-in functions directly rather than going through the default simplifications. When it does this, EvalRules will not be able to override those functions. For example, when you take the absolute value of the complex number (2, 3), Calc computes `sqrt(2*2 + 3*3)' by calling the multiplication, addition, and square root functions directly rather than applying the default simplifications to this formula. So an EvalRules rule that (perversely) rewrites `sqrt(13) := 6' would not apply. (However, if you put Calc into symbolic mode so that `sqrt(13)' will be left in symbolic form by the built-in square root function, your rule will be able to apply. But if the complex number were (3,4), so that `sqrt(25)' must be calculated, then symbolic mode will not help because `sqrt(25)' can be evaluated exactly to 5.)

One subtle restriction that normally only manifests itself with EvalRules is that while a given rewrite rule is in the process of being checked, that same rule cannot be recursively applied. Calc effectively removes the rule from its rule set while checking the rule, then puts it back once the match succeeds or fails. (The technical reason for this is that compiled pattern programs are not reentrant.) For example, consider the rule `foo(x) := x :: foo(x/2) > 0' attempting to match `foo(8)'. This rule will be inactive while the condition `foo(4) > 0' is checked, even though it might be an integral part of evaluating that condition. Note that this is not a problem for the more usual recursive type of rule, such as `foo(x) := foo(x/2)', because there the rule has succeeded and been reactivated by the time the righthand side is evaluated.

If EvalRules has no stored value (its default state), or if anything but a vector is stored in it, then it is ignored.

Even though Calc's rewrite mechanism is designed to compare rewrite rules to formulas as quickly as possible, storing rules in EvalRules may make Calc run substantially slower. This is particularly true of rules where the top-level call is a commonly used function, or is not fixed. The rule `f(n) := n f(n-1) :: n>0' will only activate the rewrite mechanism for calls to the function f, but `lg(n) + lg(m) := lg(n m)' will check every `+' operator. And `apply(f, [a*b]) := apply(f, [a]) + apply(f, [b]) :: in(f, [ln, log10])' may seem more "efficient" than two separate rules for ln and log10, but actually it is vastly less efficient because rules with apply as the top-level pattern must be tested against every function call that is simplified.

Suppose you want `sin(a + b)' to be expanded out not all the time, but only when a s is used to simplify the formula. The variable AlgSimpRules holds rules for this purpose. The a s command will apply EvalRules and AlgSimpRules to the formula, as well as all of its built-in simplifications.

Most of the special limitations for EvalRules don't apply to AlgSimpRules. Calc simply does an a r AlgSimpRules command with an infinite repeat count as the first step of a s. It then applies its own built-in simplifications throughout the formula, and then repeats these two steps (along with applying the default simplifications) until no further changes are possible.

There are also ExtSimpRules and UnitSimpRules variables that are used by a e and u s, respectively; these commands also apply EvalRules and AlgSimpRules. The variable IntegSimpRules contains simplification rules that are used only during integration by a i.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.12 Debugging Rewrites

If a buffer named `*Trace*' exists, the rewrite mechanism will record some useful information there as it operates. The original formula is written there, as is the result of each successful rewrite, and the final result of the rewriting. All phase changes are also noted.

Calc always appends to `*Trace*'. You must empty this buffer yourself periodically if it is in danger of growing unwieldy.

Note that the rewriting mechanism is substantially slower when the `*Trace*' buffer exists, even if the buffer is not visible on the screen. Once you are done, you will probably want to kill this buffer (with C-x k *Trace* RET). If you leave it in existence and forget about it, all your future rewrite commands will be needlessly slow.

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

12.11.13 Examples of Rewrite Rules

Returning to the example of substituting the pattern `sin(x)^2 + cos(x)^2' with 1, we saw that the rule `opt(a) sin(x)^2 + opt(a) cos(x)^2 := a' does a good job of finding suitable cases. Another solution would be to use the rule `cos(x)^2 := 1 - sin(x)^2', followed by algebraic simplification if necessary. This rule will be the most effective way to do the job, but at the expense of making some changes that you might not desire.

Another algebraic rewrite rule is `exp(x+y) := exp(x) exp(y)'. To make this work with the j r command so that it can be easily targeted to a particular exponential in a large formula, you might wish to write the rule as `select(exp(x+y)) := select(exp(x) exp(y))'. The `select' markers will be ignored by the regular a r command (see section 12.11.9 Selections with Rewrite Rules).

A surprisingly useful rewrite rule is `a/(b-c) := a*(b+c)/(b^2-c^2)'. This will simplify the formula whenever b and/or c can be made simpler by squaring. For example, applying this rule to `2 / (sqrt(2) + 3)' yields `6:7 - 2:7 sqrt(2)' (assuming Symbolic Mode has been enabled to keep the square root from being evaulated to a floating-point approximation). This rule is also useful when working with symbolic complex numbers, e.g., `(a + b i) / (c + d i)'.

As another example, we could define our own "triangular numbers" function with the rules `[tri(0) := 0, tri(n) := n + tri(n-1) :: n>0]'. Enter this vector and store it in a variable: s t trirules. Now, given a suitable formula like `tri(5)' on the stack, type `a r trirules' to apply these rules repeatedly. After six applications, a r will stop with 15 on the stack. Once these rules are debugged, it would probably be most useful to add them to EvalRules so that Calc will evaluate the new tri function automatically. We could then use Z K on the keyboard macro ' tri($) RET to make a command that applies tri to the value on the top of the stack. See section 19. Programming.

The following rule set, contributed by Francois Pinard, implements quaternions, a generalization of the concept of complex numbers. Quaternions have four components, and are here represented by function calls `quat(w, [x, y, z])' with "real part" w and the three "imaginary" parts collected into a vector. Various arithmetical operations on quaternions are supported. To use these rules, either add them to EvalRules, or create a command based on a r for simplifying quaternion formulas. A convenient way to enter quaternions would be a command defined by a keyboard macro containing: ' quat($$$$, [$$$, $$, $]) RET.

[ quat(w, x, y, z) := quat(w, [x, y, z]),
  quat(w, [0, 0, 0]) := w,
  abs(quat(w, v)) := hypot(w, v),
  -quat(w, v) := quat(-w, -v),
  r + quat(w, v) := quat(r + w, v) :: real(r),
  r - quat(w, v) := quat(r - w, -v) :: real(r),
  quat(w1, v1) + quat(w2, v2) := quat(w1 + w2, v1 + v2),
  r * quat(w, v) := quat(r * w, r * v) :: real(r),
  plain(quat(w1, v1) * quat(w2, v2))
     := quat(w1 * w2 - v1 * v2, w1 * v2 + w2 * v1 + cross(v1, v2)),
  quat(w1, v1) / r := quat(w1 / r, v1 / r) :: real(r),
  z / quat(w, v) := z * quatinv(quat(w, v)),
  quatinv(quat(w, v)) := quat(w, -v) / (w^2 + v^2),
  quatsqr(quat(w, v)) := quat(w^2 - v^2, 2 * w * v),
  quat(w, v)^k := quatsqr(quat(w, v)^(k / 2))
               :: integer(k) :: k > 0 :: k % 2 = 0,
  quat(w, v)^k := quatsqr(quat(w, v)^((k - 1) / 2)) * quat(w, v)
               :: integer(k) :: k > 2,
  quat(w, v)^-k := quatinv(quat(w, v)^k) :: integer(k) :: k > 0 ]

Quaternions, like matrices, have non-commutative multiplication. In other words, q1 * q2 = q2 * q1 is not necessarily true if q1 and q2 are quat forms. The `quat*quat' rule above uses plain to prevent Calc from rearranging the product. It may also be wise to add the line `[quat(), matrix]' to the Decls matrix, to ensure that Calc's other algebraic operations will not rearrange a quaternion product. See section 8.6 Declarations.

These rules also accept a four-argument quat form, converting it to the preferred form in the first rule. If you would rather see results in the four-argument form, just append the two items `phase(2), quat(w, [x, y, z]) := quat(w, x, y, z)' to the end of the rule set. (But remember that multi-phase rule sets don't work in EvalRules.)

[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by XEmacs Webmaster on October, 2 2007 using texi2html