Sympy#

%config InlineBackend.figure_format = 'svg'
import numpy as np
import matplotlib.pyplot as plt

Let us import everything from sympy. We will also enable pretty printing.

from sympy import *
init_printing()

We must define symbols which will be treated symbolically

x = symbols('x')
type(x)
sympy.core.symbol.Symbol

Let us define an expression

f = x*sin(pi*x) + tan(pi*x)
print(f)
x*sin(pi*x) + tan(pi*x)

Differentiation. We can perform symbolic differentiation.

diff(f,x)
_images/5ecb55dcaf22b9e49b3d6a26195f5731322a477f31f2a1917efe5e3c4b27c4f7.png

Compute second derivative

diff(f,x,2)
_images/4d93b0926693625c998e35c695a3a56629d119e6c4b7f1d4a2373b12ca9a3271.png

Product of two functions. Define a more complicated expression as product of two expressions

\[ h(x) = f(x) g(x) \]

and differentiate it

g = exp(x)
h = f*g
diff(h,x)
_images/d057dd9ef4039b79847c3e803a2e676a7b41929907f86f764a4d3eaeb0c462a6.png

Function composition. Composition of two functions

\[ h(x) = (f \circ g)(x) = f(g(x)) \]

Apply this to the case \(f(x) = \sin(x)\), \(g(x) = \cos(x)\).

g = cos(x)
h = sin(g)
print('h =',h)
diff(h,x)
h = sin(cos(x))
_images/2f0e52e743e8a194b9b72dce9f96bd4a0bc8671b98ee63b146eefee97ab41ef3.png

If the two functions \(f,g\) have already been defined and we want to compose, then we can use substitution,

f = sin(x)
g = cos(x)
h = f.subs({'x':g})
print('h =',h)
diff(h,x)
h = sin(cos(x))
_images/2f0e52e743e8a194b9b72dce9f96bd4a0bc8671b98ee63b146eefee97ab41ef3.png

Integration. We can also compute indefinite integrals

print(f)
integrate(f,x)
sin(x)
_images/8794a4f333c3b588125eff55ef0422f971ed09674bccd007c9acfd331e0eb631.png

We can get definite integrals.

integrate(f,(x,0,1))
_images/d855d3cc10be990a0bb8151ce3e8ba7da1556da8b305bd2206797086d199e977.png

Taylor expansion. Compute Taylor expansion of \(f(x)\) around \(x=0\)

print(f)
series(f,x,0,8)
sin(x)
_images/0490ee4cf9a4ff42ee3b3a9707389234353903f81ed0b006f45705f8aff1ea90.png

Compute Taylor expansion of \(h(x)\) around \(x=0\)

print(h)
series(h,x,0,8)
sin(cos(x))
_images/8fac84ac0b22d0be691eb82e586fddb5f6c6675d09d174856d0716833c300d58.png

Expression is not a function#

p = (x + 1)/(x**2 + 2)

We cannot evaluate p at a numerical value. To do that, we can substitute the symbol x with a numerical value.

p.subs({'x':1})
_images/7135433e2bb791c8de41d5753fa56b352a476c17577b932a2d9575893d5b5578.png
p.subs({'x':1.0})
_images/6fc997c29f4ca0f496287a8f521da38e1d2d6af6da94b4cb6278f67021393397.png

The result depends on whether we substitute with an integer or a float. You can substitute with a rational number like this

p.subs({'x':1//2})
_images/92a8cc03a63b4602487cd0a3e68f4018d1be5aac1c56f8a7882d575ba13f1793.png

For how to make python functions out of expressions, see below.

Compute roots#

Let us find roots of quadratic equation

\[ a x^2 + b x + c = 0 \]
x, a, b, c = symbols('x a b c')
eq = a*x**2 + b*x + c
roots(eq,x)
_images/ee1b06636f26dac0e386895c3cf776cd746af0ab7f1fc2c6cd97972d91389b9e.png
x, a = symbols('x a')
eq = (x - a)**2
roots(eq,x)
_images/51706dfff9efd1dd8c416772961733d73332cad8f9fc086b9ea924e80b668477.png

Expand, factor, simplify#

x, a = symbols('x a')
expand((x-a)**2)
_images/23a5922ab9e2f970aa7039d3d6a823e8f96ea11abb8f8e48347607436d6191c1.png
factor(x**2 - 2*a*x + a**2)
_images/79927107d5ccc786a574b33b3a5012e0207794c9c38b9b1351c8800f07cf8b17.png
f = (x + x**2)/(x*sin(x)**2 + x*cos(x)**2)
simplify(f)
_images/61f3e8afdb0a5deb1c8271839c0cc5fc550a8a7315ea790e7c7b3eca80fdf692.png

Get Latex code#

x = symbols('x')
p = (cos(x)-sin(x))**3
expand(p)
_images/963b4d00a9731873399b28c0416d1c1ddab83f75bae0916c04ad4748c27a9aeb.png
print_latex(expand(p))
- \sin^{3}{\left(x \right)} + 3 \sin^{2}{\left(x \right)} \cos{\left(x \right)} - 3 \sin{\left(x \right)} \cos^{2}{\left(x \right)} + \cos^{3}{\left(x \right)}

Solve a linear system#

\[ x_1 + x_2 = a, \qquad x_1 - x_2 = b \]
x1, x2, a, b = symbols('x1 x2 a b')
e1 = x1 + x2 - a
e2 = x1 - x2 - b
solve([e1, e2],[x1,x2])
_images/252fda8353bdaad778634ae5140941fca5ad2e92543c129b9fec46b9464aeab5.png

We can capture the result as a dictionary.

r = solve([e1, e2],[x1,x2])
print('x1 = ', r[x1])
print('x2 = ', r[x2])
x1 =  a/2 + b/2
x2 =  a/2 - b/2

Example: create Python function from sympy expression#

Lets first define a function and get its derivative.

x = symbols('x')
f = x * sin(50*x) * exp(x)
g = diff(f,x)

We cannot evaluate f and g since they are not python functions. We first create Python functions out of the symbolic expressions and then plot them.

ffun = lambdify(x,f)
gfun = lambdify(x,g)

We can now evaluate these functions at some argument

print('f(1) =',ffun(1.0))
print('g(1) =',gfun(1.0))
f(1) = -0.7132087970679899
g(1) = 129.72606342238427

We can now use these to plot graphs of the functions

xx = np.linspace(0.0,1.0,200)
plt.figure(figsize=(9,4))
plt.subplot(121)
plt.plot(xx,ffun(xx))
plt.grid(True)
plt.title('f(x) = '+str(f))
plt.subplot(122)
plt.plot(xx,gfun(xx))
plt.grid(True)
plt.title('Derivative of f(x)');
_images/212c261f0ded18d007e9e3d0fcca3302e58fa3a64ba66d368ea8e0c32bade896.svg

Example: Truncation error of FD scheme#

Approximate second derivative using finite difference scheme

\[ f''(x) \approx \frac{f(x-h) - 2 f(x) + f(x+h)}{h^2} \]

We perform Taylor expansion around \(x\) to find the error in this approximation: write

\[ f(x \pm h) = f(x) \pm h f'(x) + \frac{h^2}{2} f''(x) \pm \ldots \]

and simplify the rhs. This can be done by sympy.

x, h = symbols("x,h")
f = Function("f")
T = lambda h: (f(x-h) - 2*f(x) + f(x+h))/(h**2)
series(T(h), h, x0=0, n=6)
_images/5f96950fcf63815ebd93a41d38132046ab15eba3e2364cfeb6ba78f6b9ec5662.png

The above result shows that the FD formula is equal to

\[ \frac{f(x-h) - 2 f(x) + f(x+h)}{h^2} = f''(x) + \frac{h^2}{12} f^{(4)}(x) + \frac{h^4}{360} f^{(6)}(x) + O(h^6) \]

The leading error term is \(O(h^2)\)

\[ \textrm{error} = \frac{f(x-h) - 2 f(x) + f(x+h)}{h^2} - f''(x) = \frac{h^2}{12} f^{(4)}(x) + \frac{h^4}{360} f^{(6)}(x) + O(h^6) = O(h^2) \]

so that the formula is second order accurate.