Topic 6d: Symbolic Computing#

import sympy
sympy.init_printing()
import numpy as np

The emphasis in this course has been the use of computers for manipulting data or using numerical techniques to solve problems. These are central problems in scientific computing and are good windows into how computers work.

Of course, computers can be designed to do much more complex tasks and we should use computers when they are available to simplify/speed up/check our work. Symbolic computing is an excellent example of a computer resource that can assist in solving problems that we might usually associate with a human and a piece of paper. Fundamentally, the computer can do a lot of our math for us, so we should use it to check our results at the very least.

Sympy is a python library that can do a lot of math for us, in this way. It is similar to mathematica in both function and design, but is freely available. One of the most basic sources of confusion is that these symbolic tools try to avoid making any assumptions:

x=sympy.Symbol('x')
sympy.sqrt(x**2)
_images/Topic6d_Sympy_5_0.png

Sympy does not know how to simplify this expression. What if \(x\) is a complex number? We will get different answers depending on our assumptions:

y=sympy.Symbol('y',real=True)
sympy.sqrt(y**2)
_images/Topic6d_Sympy_8_0.png
y=sympy.Symbol('y',positive=True)
sympy.sqrt(y**2)
_images/Topic6d_Sympy_10_0.png
n1=sympy.Symbol('n1',integer=True)
n2=sympy.Symbol('n2',odd=True)
sympy.cos(sympy.pi*n1)
_images/Topic6d_Sympy_12_0.png
sympy.cos(sympy.pi*n2)
_images/Topic6d_Sympy_13_0.png

Manipulating Expressions#

We can define experessions you might want to work with

exp=x**2+x*(x*2+x*2)+x*(x+1)+3
exp
_images/Topic6d_Sympy_17_0.png

We can simplify these expressions two ways:

sympy.simplify(exp)
_images/Topic6d_Sympy_19_0.png
exp.simplify()
_images/Topic6d_Sympy_20_0.png

Similarly we can factor expressions

exp2=x**2+2*x+1
exp2
_images/Topic6d_Sympy_23_0.png
sympy.factor(exp2)
_images/Topic6d_Sympy_24_0.png
exp2.factor()
_images/Topic6d_Sympy_25_0.png

or expand

((x+2)**5).expand()
_images/Topic6d_Sympy_27_0.png
sympy.sin(x+y).expand(trig=True)
_images/Topic6d_Sympy_28_0.png

Evaluating Expressions#

In a lot of situations, we need to combine our symbolic work with something numerical. Sometimes, this means we just want to force the computer to return a demical instead of a symbolic expression for the number:

sympy.N(sympy.pi)
_images/Topic6d_Sympy_31_0.png
sympy.N(sympy.pi,50) # tell it you want 50 decimal places
_images/Topic6d_Sympy_32_0.png
sympy.cos(1)
_images/Topic6d_Sympy_33_0.png
sympy.N(sympy.cos(1))
_images/Topic6d_Sympy_34_0.png

In other situations, we have found a function that we want to be able to evaluate numerically:

exp
_images/Topic6d_Sympy_36_0.png

We can just substitute \(x\) with a number (or another variable) if we like

exp.subs(x,10)
_images/Topic6d_Sympy_38_0.png

But we can also define an actual function we can use as normal with lambdify

exp_fnc=sympy.lambdify(x,exp)
exp_fnc(10)
_images/Topic6d_Sympy_41_0.png
x_array=np.linspace(1,10,10)
exp_fnc(x_array)
array([ 10.,  29.,  60., 103., 158., 225., 304., 395., 498., 613.])
exp3=sympy.sin(x**2)
exp_fnc3=sympy.lambdify(x,exp3)
exp_fnc3(x_array)
array([ 0.84147098, -0.7568025 ,  0.41211849, -0.28790332, -0.13235175,
       -0.99177885, -0.95375265,  0.92002604, -0.62988799, -0.50636564])

Solving Equations#

Not suprisingly, sympy is pretty good at solving equations.

sympy.solve(x**2-1)
_images/Topic6d_Sympy_49_0.png
sympy.solve(sympy.sin(x))
_images/Topic6d_Sympy_50_0.png
sympy.solve([x+y+1,x+2*y],[x,y])
_images/Topic6d_Sympy_51_0.png

Calculus#

Calculus is one of the symbolic tools that tends to be very powerful and reliable. In some cases it would be enormously difficult to do it yourself yet the computer seems to find a nice answer quickly.

Derivatives#

exp4=sympy.sin(x*sympy.cos(x**2 +x+2))
exp4
_images/Topic6d_Sympy_55_0.png
sympy.diff(exp4)
_images/Topic6d_Sympy_56_0.png
exp4.diff(x)
_images/Topic6d_Sympy_57_0.png
exp4.diff(x,x).simplify()
_images/Topic6d_Sympy_58_0.png
exp5=x**2*y+y**3*x+x+3*y
exp5.diff(y)
_images/Topic6d_Sympy_60_0.png
exp5.diff(x)
_images/Topic6d_Sympy_61_0.png
exp5.diff(x,y)
_images/Topic6d_Sympy_62_0.png

Series#

An extremely useful tool when trying to understand functions is the Taylor series around a given point

exp4.series(x,0,3) # this means: series wrt x, at x=0 to order x^3.
_images/Topic6d_Sympy_65_0.png

This becomes much more useful when you start dealing with functions you don’t konw a lot about

sympy.besselj(2,x).series(x,0,3)
_images/Topic6d_Sympy_67_0.png

Importantly, series is not really a Taylor series. In includes inverse powers as well:

sympy.gamma(x).series(x,0,3)
_images/Topic6d_Sympy_69_0.png

To see why this is useful consider

(sympy.gamma(x)*sympy.sin(x)).subs(x,0)
_images/Topic6d_Sympy_71_0.png
(sympy.gamma(x)*sympy.sin(x)).series(x,0,2)
_images/Topic6d_Sympy_72_0.png

The function \(\Gamma[x]\sin(x)\) is perfectly well behaved at \(x=0\) but you need to look at the series as \(x\to 0\) to see that. There is also a limit option but in my experience series is always better than limit.

Integration#

Integration is where it is really obvious how much better a computer is than a person. Even just a minor change to an integral can make it faily challenging

sympy.integrate(sympy.sin(x),x)
_images/Topic6d_Sympy_76_0.png
sympy.integrate(sympy.sin(x**2),x)
_images/Topic6d_Sympy_77_0.png
sympy.integrate(x**2*sympy.exp(-x),(x,0,1))
_images/Topic6d_Sympy_78_0.png

The notation for \(\infty\) is a bit unusual

sympy.integrate(sympy.exp(-x**2),(x,0,sympy.oo))
_images/Topic6d_Sympy_80_0.png

We can also perform a integration that produces a function of some other variable:

a=sympy.Symbol('a')
sympy.integrate(sympy.exp(-a*x**2),(x,0,sympy.oo))
_images/Topic6d_Sympy_83_0.png

Notice again that it didn’t know what to do with our variable \(a\) (it could be real, imaginary, complex, negative, postive. Fortunately, we can tell it what to assume to avoid this problem (sometimes it can’t figure it out on its own):

c=sympy.Symbol('c',positive=True)
sympy.integrate(sympy.exp(-c*x**2),(x,0,sympy.oo))
_images/Topic6d_Sympy_86_0.png

Summary#

Symbolic computation is extremely powerful and useful. The functionality is really quite endless. However, one often does not appreciate all that it can do until you really need it. At the very least, it will keep you from making stupid mistakes on your homework.