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 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 Mode Settings, for discussions of these modes. You may also wish to select "big" display mode (d B). See section Normal Language Modes.
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.
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 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 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 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 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.
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.
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
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
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 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 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 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 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 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 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 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 Rewrite Rules.
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 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 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 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 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 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 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).
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 Rearranging Formulas using Selections. See section Rewrite Rules.
See section Simplification Modes, for commands to control what level of simplification occurs automatically. Normally only the "default simplifications" occur.
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 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 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 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 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 @c{$x^{a+b}$} 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: @c{$x^{-2} y$} 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 Infinite Mode.
The expression @c{$a / b^{-c}$} a / b^(-c) is changed to a b^c, where -c is any negative-looking power. Also, 1 / b^c is changed to @c{$b^{-c}$} 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, @c{$((-3)^{1/2})^2$} ((-3)^1:2)^2 is safe to simplify, but ((-3)^2)^1:2 is not.) See section 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 @c{$abs(a^{b c})$} 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 @c{$1 / x^{1:2}$} 1 / x^1:2 is changed to @c{$x^{-1:2}$} x^(-1:2), but 1 / sqrt(x) is left alone.
Generic identity matrices (see section 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 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 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.
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 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 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 @c{$2 \pi$}
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)), @c{$e^{\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 @c{$\pi i$}
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 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.
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 @c{$x^{a b}$} x^(a b) for all a and b. These results will be valid only in a restricted range of x; for example, in @c{$(x^2)^{1:2}$} (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 @c{$x^{a/2}$} 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.
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 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 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 @c{$a^{b c}$} 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 @c{$(4046 m^2)^{1.5}$}
(4046 m^2)^1.5, which is then
changed to @c{$4046^{1.5} \, (m^2)^{1.5}$}
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.
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 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 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 Decomposing Polynomials, for several useful functions for extracting the individual coefficients of a polynomial.
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.
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 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 @c{$x=x_0$}
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 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).
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; @c{$x/(1+x^{-2})$}
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.
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 @c{$1/(x+\sqrt{x^2+1})$} 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 @c{$1/(1+\tan x)$} 1/(1+tan(x)) differs from the solution given in the CRC Math Tables by a constant factor of @c{$\pi i / 2$} 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 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 Rewrite Rules.
Calc has two built-in rewrite rules called IntegRules
and
IntegAfterRules
which you can edit to define new integration
methods. See section 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 @c{${\rm Ei}(x)$}
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 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
.
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.
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.
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 @c{$a < b \, c$} a < b c for b is impossible without knowing the sign of c. In this case, a S will produce the result @c{$b \mathbin{\hbox{\code{!=}}} a/c$} b != a/c (using the not-equal-to operator) to signify that the direction of the inequality is now unknown. The inequality @c{$a \le b \, c$} a <= b c is not even partially solved. See section 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 Logical Operations), which converts x = y/3 - 2 to y/3 - 2, and s l (see section The Let Command) which evaluates another formula with x set equal to y/3 - 2.
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 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 coefficents
are all numbers (real or complex).
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 Curve Fitting.
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.
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 Curve Fitting, for some other, more specialized, operations on numerical data.)
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.
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 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 @c{$x \sin x$} x sin(x), have infinitely many. In fact, there is no easy way to define the "global" minimum of @c{$x \sin x$} 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.
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.
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.
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 @c{$2\times N$} 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 @c{$N\times2$} Nx2 matrix instead of a @c{$2\times N$} 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
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 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
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 @c{$\chi^2 \ge 0$} chi^2 >= 0. Least-squares fitting simply chooses the values of a and b for which the error @c{$\chi^2$} 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.
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 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.
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 +/- @c{$\sigma_i$} sigma_i', then the @c{$\chi^2$} chi^2 statistic is now,
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 @c{$\sigma_i$} 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 @c{$\sigma_i$} 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 (@c{$\chi^2$} chi^2 is simply scaled uniformly by @c{$1 / \sigma^2$} 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:
utpc
probability distribution
function using @c{$\chi^2$}
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, @c{$\chi^2$}
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 @c{$\chi^2$}
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.
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:
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 @c{$3\times360$}
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.)
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 @c{$Y = \ln(y)$} Y = ln(y), @c{$A = \ln(a)$} A = ln(a), F = 1, B = b, and @c{$G = \ln(x)$} 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 @c{$a = \exp(A)$} 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 @c{$\chi^2$}
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 @c{$\chi^2$}
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 @c{$\sigma_i$} 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 @c{$\sigma_i$} 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 Other Operations on Variables.
See section 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
fitsystem(ln(fitvar(2)), [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 fitpart
s
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.
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.)
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 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 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 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 @c{$k \ne k_0$} 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]'.
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 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 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 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.)
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 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.
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:
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 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 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.
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 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.
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 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 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.)
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 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 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)'.
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 @c{$v_1$} v1, as either a variable name or a function name, replaced with @c{$x_1$} x1 and so on. (If @c{$v_1$} v1 is used as a function name, then @c{$x_1$} x1 must be either a function name itself or a `< >' nameless function; see section 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:
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))'.
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.
cons
, except that the last element
is matched to h, with the remaining elements matched
to t.
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 Conditional Rewrite Rules.
Special functions for the righthand sides of rules are:
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 Conditional Rewrite Rules, for another trick for
protecting rules from evaluation.)
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))'.
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.
cons
except putting h at the end of
the vector t.
apply
is also a regular Calc function.
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]'.
There are also some special functions you can use in conditions.
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))
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.
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.
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.
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!
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.
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), phase(1), f1(x) := g1(x), phase(2), f2(x) := g2(x), phase(3), f3(x) := g3(x), phase(1,2), 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 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 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.
If a sub-formula of the current formula is selected (as by j s;
see section 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 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.
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.
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 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.
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.
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 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 Programming.
The following rule set, contributed by @c{Fran\c cois}
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 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
.)