# Python Symbolics

In the twenty-first century there is little or no reason to compute complicated integrals and derivatives by hand. We can instead use any of various python packages to do exact arithmetic, perform algebraic operations, and evaluate limits, integrals and other calculus constructions easily. One package for python that is somewhat mature is called Sage. This depends upon python, but builds on top of it, rather than being a simple package such as numpy. We will instead use sympy, a relatively new and less than mature package with a bright future. Ultimately, the point is that symbolic math packages are a dime a dozen these days, and learning one will demonstrate most of the ideas anchoring all of them, though the details of syntax vary from one package to another.

The first thing to note about sympy is that it prefers to treat numbers as exact. This means that each number is an object in itself, with various means of interpretation, including approximation to an arbitrary number of digits of accuracy. Thus, numpy provides a fixed floating point approximation to π, but sympy treats π as a real number that can be evaluated in floating point arithmetic to any number of digits you require.

>>> from numpy import * >>> pi 3.141592653589793 >>> from sympy import * >>> pi pi >>> pi.evalf() 3.14159265358979 >>> pi.evalf(50) 3.1415926535897932384626433832795028841971693993751

There are several significant things here.
First, when you do arithmetic with sympy objects
you might not get the simple numbers you expect - learn to change
your expectations. The second thing is that working with
symbolic objects will be *much* slower than working with
ordinary python numbers. Do not use sympy objects unless you
really need to do exact or symbolic calculations.

Another thing to be careful with here is that if it is not clear
that the object we are dealing with is a sympy object, then ordinary
python rules apply. Thus, `1/3` is treated as an integer
arithmetic operation, with result `0`. Likewise, `1.0/3` is
treated as a floating point operation, with result `0.3333333333333333`.
However, if we want to do an exact calculation, we must make
a rational expression. As soon as python realizes it is working
with one rational, it treats an entire expression as if it is rational.

>>> q=Rational(1,3) >>> q 1/3 >>> q.evalf(50) 0.33333333333333333333333333333333333333333333333333 >>> q=Rational(1)/3 >>> q 1/3

The whole point of working with a symbolic package is to permit
us to do calculations with symbolic variables. Sympy allows us
to define symbolic variables and then work with them. There are two
ways to start this: we can use the `symbols` command, or we
can use the `var` command. New variables formed using
symbolic variables are themselves symbolic.

>>> x,y=symbols('x,y') >>> var('u,v') (u, v) >>> x-x 0 >>> x+x 2*x >>> expr=(u+v)**4 >>> expr (u + v)**4

Symbolic variables have many functions associated with them that allow
us to do various algebraic and calculus operations.
The whole point of using e.g. the `var` command to declare
the variable was for python to create an entire class around that name,
together with associated mathematical associations and operations.
We get to these using the usual method notation: the variable name,
followed by a period and the name of the method we want to apply.
For example, the
`expand` function expands polynomial expressions. We can reverse
this by using `factor`. If we need to substitute one expression
for another, there is a `subs` method to do that.

>>> newexpr=expr.expand() >>> newexpr u**4 + 4*u**3*v + 6*u**2*v**2 + 4*u*v**3 + v**4 >>> newexpr.factor() (u + v)**4 >>> newexpr.subs(u,2) v**4 + 8*v**3 + 24*v**2 + 32*v + 16 >>> (newexpr.subs(u,2)).factor() (v + 2)**4

Notice in the last expression that we simply put parentheses around a computed quantity, which was symbolic because it was created from symbolic expressions, and then used a method associated with this new symbolic object. Once again for emphasis: any time we perform a computation that uses a symbolic variable, the result is itself symbolic, and hence has access to all the methods associated with symbolic variables.

When we use symbolic expressions, we really don't want to have to look at all that "**" stuff. We want to see something that resembles ordinary mathematical notation. Many symbolic packages call this "pretty printing". Regrettably, the result is not always pretty, but most of the time it produce result that are more legible than the straight python syntax. To do pretty printing using sympy, run three commands in your session:

>>> import sys >>> oh=sys.displayhook >>> sys.displayhook=pprintAfter that is done, your expressions should be "pretty printed".

>>> u=1/((y+3)*(y-4)**2) >>> u 1/((y - 4)**2*(y + 3)) >>> import sys >>> oh=sys.displayhook >>> sys.displayhook=pprint >>> u 1 ---------------- 2 (y - 4) *(y + 3)

In all further discussion of the sympy package, we will suppose that you have run these commands.

Sympy can manipulate fractional expressions. For example,
it contains a command that every symbolic manipulation program in
history has had: `simplify`.

>>> simplify(newexpr/(u+v)) 3 2 2 3 u + 3*u *v + 3*u*v + v >>> (newexpr/(u+v)).simplify() 3 2 2 3 u + 3*u *v + 3*u*v + v >>> trigexp=sin(u)**2+cos(u)**2 >>> trigexp 2 2 sin (u) + cos (u) >>> trigexp.simplify() 1

Simplify looks for common factors in the numerator and denominator of an expression and cancels them if they are found. It also tries to apply some simple rules about exponents and trigonometric functions.

Another requirement in dealing with fractional expressions is combining
and breaking up expressions with different denominators. For this
sympy gives us `together` and `apart`. The point is that
`apart` is doing partial fraction decompositions for you.

>>> expr = 1/((u+1)*(u-3)) >>> newexpr=apart(expr) >>> newexpr 1 1 - --------- + --------- 4*(u + 1) 4*(u - 3) >>> together(newexpr) 1 --------------- (u - 3)*(u + 1)

Assignment C is posted.

The test solution is available.