This notebook is part of a draft of a set of elementary Mathematica tutorials, as presented at the 1999 Mathematica Developer Conference, October 20-23, 1999. This draft is provided here to encourage suggestions about these tutorials. You can send suggestions about these tutorials to Wolfram Research Technical Support at the address support@wolfram.com.

Only the first nine parts of the Basic Tutorial are included in this draft.

Basic Tutorial (part 3 of 10)

3 Symbolic Calculations

In this third part of the basic tutorial you will learn about:

• Basic polynomial operations
• Algebraic equations
• Symbolic calculus
• Simplification
• Assumptions about parameters

Polynomials

Nearly all symbolic calculations are based on operations with polynomials and rational functions. Many of the characteristics of functions such as Solve, Simplify, and Integrate follow from the characteristics of the underlying polynomial functions.

A polynomial is an expression constructed from sums, products, and positive integer powers of variables and constants. For example, x^2 + 3  x y + 2  y^2 is a polynomial in the variables x and y.

A rational function is a ratio of polynomials. For example, 1/(x^2 + 3  x y + 2  y^2) is a rational function.

Two basic operations on polynomials are Factor and Expand.

expr=Factor[x^2 + 3  x y + 2  y^2]

Expand[expr]

Two basic operations on rational functions are Together, which combines the terms in a sum of rational functions over a common denominator, and Apart, which does partial fraction decomposition.

expr = 1/(x + y) + 1/(2  x + y) + 1/(3  x + y)

expr = Together[expr]

The Apart function takes a second argument which specifies the variable with respect to which the partial fraction decomposition is done. Apart can also be used with one argument, in which case the variable is chosen automatically. The one-argument form of Apart is not recommended unless your expression includes only one variable. Here is an example where the second argument is included explicitly to avoid any ambiguity.

Apart[expr, x]

The variables in a polynomial do not need to be symbols. For example, the expression 2 + 3  Cos[x] + Cos[x]^2 can be treated as a polynomial in the variable Cos[x].

Factor[2 + 3  Cos[x] + Cos[x]^2]

Roots and algebraic expressions

Many calculations require computing roots of polynomials. Mathematically, any expression that can occur as a root of a polynomial is called an algebraic expression. For example, Sqrt[c] is an algebraic expression because it is a root of the polynomial x^2 - c. If all of the constants in an algebraic expression are numbers, the expression is called an algebraic number. For example, Sqrt[2], which is a root of the polynomial x^2 - 2, is an algebraic number.

A radical is an expression with an exponent that is a rational number (a ratio of integers). Radicals are frequently used to represent roots of polynomials. Sqrt[2], or 2^(1/2), where the exponent is the rational number 1/2, is both a radical and an algebraic number.

In many practical examples the distinctions between polynomials, algebraic expressions, radicals, and algebraic numbers can be ignored. The Solve function, for example, will automatically introduce algebraic expressions as needed to represent a solution. Here is an example in which Solve introduces the algebraic number Sqrt[2] to represent the solutions of the equation x^2 - 2 == 0.

Solve[x^2 - 2 == 0, x]

It is worth noting that not all algebraic expressions (roots of polynomials) can be represented using radicals. The roots of the polynomial x^5 - x + 1, for example, cannot be represented using radicals. Instead, Mathematica uses expressions based on Root to represent the roots of this polynomial.

sol = Solve[x^5 - x + 1 == 0, x]

You can use Root expressions just as you would use any other expression. For example, you can use the N function to compute numerical approximations for the Root expressions that are used to represent the solutions of x^5 - x + 1 == 0.

N[sol]

For reasons involving the mathematics of the underlying algorithms, Factor and related functions (such as Apart) do not by default introduce algebraic expressions that were not present in the original input. For example, Factor leaves the polynomial 2 - x^2 unfactored, even though this polynomial can be factored as (Sqrt[2] - x) (Sqrt[2] + x).

Factor[2 - x^2]

This polynomial can be factored by using the Extension option to specify the algebraic expression that is needed to factor this polynomial.

Factor[2 - x^2, Extension -> Sqrt[2]]

Inexact numbers

Inexact numbers can lead to a variety of difficulties in symbolic calculations, for fundamental mathematical reasons unrelated to Mathematica. Although Mathematica functions are designed where possible to give reasonable results when inexact numbers occur in input, inexact numbers should generally be avoided in symbolic calculations unless they provide some advantage and you have reason to believe that the inexact numbers in your examples will not lead to difficulties.

The origin of the difficulties with inexact numbers can be understood by recalling that an inexact number is a number for which only a finite number of digits are known. The machine number 2.0, for example, represents any number that is equal to 2 to within the precision of machine numbers. The actual number might be slightly larger or slightly smaller than that nominal value. Many symbolic calculations are sensitive to the exact values of numbers.

For example, although the polynomial 2 + 3  x + x^2 is easily factored, the Factor function leaves the corresponding polynomial with an inexact exponent unchanged. This polynomial cannot be factored if the exponent is slightly larger or slightly smaller than 2.

Factor[2 + 3  x + x^2]

Factor[2 + 3  x + x^2.0]

Another problem with inexact numbers can be illustrated by observing that a small change in an inexact coefficient can cause repeated factors of a polynomial to become distinct factors. For example:

Factor[1 + 2.0  x + x^2]

Factor[1 + 2.0000000001  x + x^2]

In operations such as symbolic integration that are sensitive to the distinction between repeated factors and distinct factors, this effect can have a dramatic effect on the results of these calculations.

Many symbolic functions, such as Solve, are designed to return results that are correct to within numerical error when the input includes inexact numbers.  For example:

Solve[2 + 3  x + x^2.0 == 0, x]

Even when the results are correct to within numerical error, however, symbolic calculations are almost faster and more reliable if all of the inputs are exact.

Algebraic equations

The basic function for solving algebraic equations in Mathematica is Solve. The first argument in Solve gives an equation or list of equations, and the second argument gives a variable or list of variables for which to solve. Equations are represented using the Equal function, which can be entered by typing two = signs between the left and right sides of each equation.

Here is an example showing the use of Solve to solve a pair of equations.

sol = 
      Solve[{x (x + y) == c, y - 2 == 0}, {x, y}]

The result from Solve is a list of solutions, each of which is a list of rules. These rules can be used to make substitutions in other expressions. For example, you can check the solutions by substituting the solutions back in to the original equations. When the left and right sides of an equation are identical, the equation will evaluate to True. In this example, the Simplify function can be used to perform the transformations that cause the right and left sides of the equation to be identical.

expr = {x (x + y) == c, y - 2 == 0} /. sol

Simplify[expr]

The result from Solve can also be used to assign values to the variables. For example, the following assignments take the first solution in the previous example and assign corresponding values to x and y.

x = x /. sol[[1]]

y = y /. sol[[1]]

Having made those assignments, however, you should keep in mind that x and y now have values that may interfere with subsequent calculations. The original Solve input, for example, will not work after making these assignments, in part because the values of x and y are not valid variables. If you wan to see this effect, try evaluating that input after evaluating the assignments for x and y.

Solve[{x (x + y) == c, y - 2 == 0}, {x, y}]

Now clear the values of x and y to avoid interference with subsequent examples.

Clear[x,  y]

If the algebraic equations are linear, it is common to omit the variables and to describe the equations using a matrix containing the coefficients. Linear equations that are specified in this way can be solved using LinearSolve. Here is an example showing the use of Solve and LinearSolve to solve the same pair of linear equations.

Solve[{c11 x + c12 y == b1, c21 x + c22 y == b2}, {x, 
        y}]

LinearSolve[{{c11, c12}, {c21, c22}}, {b1, b2}]

Symbolic derivatives

Symbolic derivatives are computed using the D function. For example, D[x Cos[x], x] gives the derivative of x Cos[x] with respect to x.

D[x Cos[x], x]

Derivatives of unknown functions, or functions for which D does not have explicit formulae, are represented as Derivative expressions. For example, this input gives the second derivative of x f[x] with respect to x. The result is shown using the notation f'[x] for Derivative[1][f][x], and f''[x] for Derivative[2][f][x].

D[x f[x], x, x]

You can use FullForm to see the corresponding Derivative expressions. For example:

FullForm[D[x f[x], x, x]]

This notation involve ' (prime) can also be used for input. For example, Cos'''[x] gives the third derivative of the Cos function.

Cos'''[x]

For derivatives of functions with more than one variable, Derivative expressions are formatted using superscripts to indicate the derivatives. For example:

expr = D[f[x, y], x, x, y]

FullForm[expr]

Symbolic integration

Symbolic integrals are computed using the Integrate function.

The first argument in Integrate is the integrand. If the second argument is a variable, Integrate gives the indefinite integral (anti-derivative) of the integrand. For example:

int = Integrate[x Cos[x], x]

You can use D to differentiate this result and check that it is a correct antiderivative of the original integrand.

D[int, x]

Definite integrals are specified by giving as the second argument in Integrate a list that specifies the variable and the limits of integration. For example:

Integrate[Sin[x], {x, x1, x2}]

Integrate can be used to do multiple integrals by including additional arguments. For example, here is a triple integral of a constant integrand. The integral specified by the last argument is done first.

int = 
      Integrate[
        1, {x, 0, 1}, {y, -x, x}, {z, -Sqrt[x^2 + y^2], 
          Sqrt[x^2 + y^2]}]

You can check this result by comparing it with a result for the corresponding numerical integral. The numerical integral can be entered by replacing Integrate with NIntegrate.

N[int]

NIntegrate[
      1, {x, 0, 1}, {y, -x, x}, {z, -Sqrt[x^2 + y^2], 
        Sqrt[x^2 + y^2]}]

Some definite integrals involving symbolic parameters will give results in the form an If expression that provides conditions on the parameters. For example, the result b for this integral is correct if the condition Re[b] > 0 in the first argument of the resulting If expression is True.

Integrate[Exp[-x/b], {x, 0, Infinity}]

You can also specify conditions on parameters using the Assumptions option. For example, this input computes the definite integral with the assumption that b > 0 is True.

Integrate[Exp[-x/b], {x, 0, Infinity}, 
      Assumptions -> b > 0]

Symbolic differential equations

Symbolic solutions of differential equations can be computed using the DSolve function. The first argument in DSolve is an equation or list of equations. Equations are entered using Equal, and derivatives are entered using Derivative expressions. The second argument in DSolve gives a function or list of functions for which to solve, and the third argument gives the independent variable.

Here is an example showing the use of DSolve to solve a differential equation involving one function. The result is a list of solutions, and each solution is a list of rules. In this particular example there is only one solution, and that solution involves a rule for only one function, but this same form of result can also be used to represent results that involve more than one solution or rules for more than one function. The constant C[1] was generated by DSolve to represent the arbitrary constant in this solution.

DSolve[f'[x] == f[x], f[x], x]

Initial conditions and boundary conditions can be included by including additional equations. Here is an example in which the differential equation f''[x] + f[x] == 0 is solved with the initial conditions f[0] == 1 and f'[0] == 0.

DSolve[{f''[x] + f[x] == 0, 
        f[0] == 1, f'[0] == 0}, f[x], x]

The independent variable can be omitted in the function specification in the second argument of DSolve, in which case the result is returned using Function expressions. Function expressions are used in Mathematica for representing pure functions. Pure functions will be discussed in Part 7 of this tutorial, and are also described in Section 2.2.5 of The Mathematica Book.

Here is an example in which the result from DSolve is returned using a Function expression. The only difference between this input and the previous input is the use of f rather than f[x] as the second argument in DSolve. The Function expression is displayed here using the notation Cos[#1] & in which #1 denotes the variable and the & character identifies this expression as a Function expression. In FullForm notation, Cos[#1] & is Function[Cos[Slot[1]]].

DSolve[{f''[x] + f[x] == 0, 
        f[0] == 1, f'[0] == 0}, f, x]

A solution using Function expressions is often more convenient that the alternative because it avoids the need to make separate substitutions for the variable and for the function. To illustrate this difference, here are two equivalent substitutions that compute the value of a solution at a point. The first input uses a typical DSolve result based on a Function expression, and the second input uses a rule that includes the independent variable explicitly. In the first input, the Function expression is applied automatically to the argument of the function. In the second input, a separate substitution is needed to insert a value for the independent variable.

f[1] /. {f -> (Cos[#1] &)}

f[x] /. {f[x] -> Cos[x]} /. x -> 1

Rules returned by DSolve can be used just like rules from any other source. Here is an example in which the result from DSolve includes rules for two functions, both expressed using Function expressions.

sol = 
      DSolve[{f'[x] ==  
            g[x] - 5 f[x], g'[x] == -4 f[x], f[0] == 0, 
          g[0] == 1}, {f, g}, x]

This gives the value of one of the solution function g[x] when x is 2.5.

g[2.5] /. sol[[1]]

Similar substitutions can be used to produce a plot showing both functions in the solution.

Plot[{f[x] /. sol, g[x] /. sol}, {x, 0, 3}]

Transforming and simplifying expressions

Any non-trivial symbolic expression can be written in a variety of mathematically equivalent forms. The preferred form is often different for different people or in different applications.

As might be expected, there is no one function in Mathematica that will always give the form that you want. Mathematica instead provides a variety of functions that invoke collections of related transformations or that convert expressions into particular forms. If the form that you want happens to be the form provides by one of these functions, then getting the form that you want will be relatively easy. Sometimes you can get a form that you want by applying a combination of functions. In difficult cases you many need to invent your own transformations.

To demonstrate the use of some of these functions, here is an expression that might come about as the result of a symbolic calculation.

expr = 
      1 + b Sin[(b - c) x]/(2 (b - c)) - 
        c Sin[(b - c) x]/(2 (b - c)) + 
        b Sin[(b + c) x]/(2 (b + c)) + 
        c Sin[(b + c) x]/(2 (b + c))

One way to simplify this expression is to use Together to collect the terms in this expression over a common denominator. In this example the denominators happen to cancel with factors from the numerator, leaving a result that is significantly simpler than the original expression.

Together[expr]

This expression can also be simplified by applying various trigonometric identities. The functions TrigExpand, TrigFactor, and TrigReduce are frequently useful for transforming expressions that contain trigonometric functions. Although these functions will convert the expression into a reasonably regular form, the result will not necessarily be any simpler than the original expression. For example:

TrigExpand[expr]

This expression can, however, with the use of combinations of functions, be simplified beyond the expression provided by Together alone. There are several combinations of functions that will give the same result. For example:

Together[TrigExpand[expr]]

This form can also be obtained using the Simplify function.

Simplify[expr]

The Simplify function works by applying transformations to the expression and returning the smallest result that it finds. In most cases the overwhelming majority of the transformations attempted by Simplify do not make the expression smaller, and the results are discarded. Simplify is consequently a relatively inefficient function, and can be prohibitively time-consuming for large expressions, but it is useful if you do not want take the time to identify specific transformation to cause the desired effect.

Simplification with assumptions

Mathematica assumes by default that all unknown symbolic parameters represent arbitrary complex numbers. The optional second argument in Simplify can be used to provide more restrictive assumptions about symbolic parameters in the expression to be simplified. Many useful transformations that do not apply for arbitrary complex values of symbolic parameters can be applied in the presence of the necessary assumptions.

For example, the expression Log[Exp[x]] is not equal to x for all complex values of x, but this expression can be simplified to x if x is known to be a real number. The assumption that x is a real number can be specified by including Element[x,Reals] in the second argument of Simplify.

Simplify[Log[Exp[x]]]

Simplify[Log[Exp[x]], Element[x, Reals]]

Another common transformation is replacing Sqrt[x^2] by x, which is not a valid replacement in general, but which is true of x is positive.

Simplify[Sqrt[x^2], x > 0]

The second argument in Simplify can be a list of assumptions. Here is an example showing a relatively complicated simplification with the assumption Element[n, Integers] , which specifies that n is an integer, and the assumption x > 1.

Simplify[
      Sqrt[x^2 + 2*x*Cos[x]^2 + Cos[x]^4 + Sin[x]^4 + 2*x*Sin[2*n*Pi + x]^2 + 
          2*Cos[x]^2*Sin[2*n*Pi + x]^2], 
      Element[n, Integers] && x > -1]

For more information about simplification with assumptions, see the documentation for Simplify, or section 3.3.10 in The Mathematica Book.

Another function that performs some of these transformations is PowerExpand. For example:

PowerExpand[Log[Exp[x]] + Sqrt[y^2]]

Unlike Simplify, however, PowerExpand does not check assumptions about parameters. The PowerExpand functions simplify invokes a few simple transformations, without checking the validity of those transformations. PowerExpand should be used only when you have reason checked for yourself that these transformations are correct in your examples.

Transformation with complex numbers

Explicit complex numbers arise naturally in many symbolic calculations. Sometimes complex numbers are unavoidable, even when the results are real, and sometimes they are simply a natural consequence of the way that the underlying algorithms work.

Although there are no general computational or mathematical reasons to avoid complex numbers, there common practical reasons to avoid them, such as if the results will be used in a program that is not designed to handle explicit complex numbers, or in academic situations where complex numbers haven't yet been introduced,

All of the functions that have been discussed are occasionally useful in transforming expressions into forms that do not contain explicit complex numbers. Three functions that are more directly useful for this purpose are ComplexExpand, ExpToTrig, and TrigToExp.

ComplexExpand is the only standard symbolic function in Mathematica which by default assumes that symbolic expressions are real. For example:

ComplexExpand[Re[x] + I Im[x]]

TrigToExp replaces trigonometric and hyperbolic functions with equivalent forms involving exponentials. For example:

expr = TrigToExp[Cos[x]]

ExpToTrig invokes the opposite identities. If the complex exponentials in this last result had arisen as the output of a symbolic calculation, ExpToTrig could be used to transform that result to a form without explicit complex numbers.

ExpToTrig[expr]

TrigToExp also replaces inverse trigonometric and inverse hyperbolic functions with equivalent forms involving logarithms. For example:

expr = TrigToExp[ArcTan[x]]

This expression can be converted by to the original form using ComplexExpand.

ComplexExpand[expr, TargetFunctions -> {Re, Im}]

Exercises

Exercise 3.1 Together and Apart

Combine the terms in this sum over a common denominator, and then convert the resulting expression back to the original form. The terms can be combined using Together, and the original form can be restored using Apart.

expr = Sum[1/(k + x), {k, 5}]

Exercise 3.2 Solving equations

Solve this pair of equations for x and y and show that the solution satisfies the equations. You can show that the solution satisfies the equations by substituting the solution back in to the equations and using Simplify to show that the resulting equations evaluate to True.

eq = {x^2 + y == c, x + b y == 0}

Exercise 3.3 Integration and differentiation

Compute the indefinite integral of this expression with respect to x, and then use differentiation to check the result. You can check the result either by observing that the derivative of the integral is equivalent to the original integrand, or by showing that the difference between the integrand and the derivative of the integral simplifies to zero.

f = Sqrt[1 + Exp[x]]

Exercise 3.4 Solving linear equations

User LinearSolve to find a solution to this system of four linear equations. You can construct the coefficient matrices for the input to LinearSolve directly from these equations, although it is possible avoid much of that effort by identifying the necessary expressions in the inputs that are used here to construct the equations.

m = (b IdentityMatrix[4] - {{0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 
                1}, {1, 0, 0, 0}});
eq = Thread[m . {x1, x2, x3, x4} == {1, 0, 0, 0}]

Exercise 3.5 Alternatives to Simplify

Find a function other than Simplify that will simplify this expression to 1.

expr = 
      Sqrt[x + y]/(1 + Sqrt[x + y]) + (1 - Sqrt[x + y])/(1 - x - 
              y)

Exercise 3.6 Simplification with assumptions

Use Simplify to show that this expression is zero when x is positive and n is an integer.

expr = Sqrt[x^2] - x Cos[2  n Pi]

Exercise 3.7 Trigonometric transformations

Use ExpToTrig and TrigExpand to write this expression as a polynomial in the variables Cos[x] and Sin[x].

expr = Exp[2  I x] + Exp[-2 I x] + Sin[2  x]

Exercise 3.8 Assumptions in Integrate

Set a value for the Assumptions option in this integral so that the result is returned without using an If expression.

Integrate[Exp[-b x]/(x + c), {x, 0, Infinity}]

Exercise 3.9 Using results from DSolve

Plot the solution of this differential equation for x ranging from 0 to 25.

sol = 
      DSolve[{f''[x] + f[x] == 0, 
          f[0] == 0, f'[0] == 1}, f, x]

Exercise 3.10 Transformations involving complex numbers

The TrigToExp function rewrites ArcSin[x] in terms of complex numbers and a logarithm.

expr = TrigToExp[ArcSin[x]]

Show how to use FullSimplify to transform this result back to ArcSin[x]. The FullSimplify function tries a much larger class of transformations than Simplify.