Sympy I-Introduction

JunjieCai

2022-05-22

中文

It is recommended to download the .ipynb file and related resources here. Then you can test the codes in the article interactively while you are reading in Jupyter.

In [9]:
from IPython.display import display
from sympy import *

Preliminaries

To understand this notes, the reader should already knows the basics of python and has a decent mathmetical backgound about function, calculus and matrix.

Helper function

In later notes, we will frequently compare the Sympy object with the result after a Sympy function is applied to it, calling a method of it, or after it is evaluated. To reduce code duplication, I write a helper.py to do these staff.

In [10]:
from helper import comparator_factory
func_comparator = comparator_factory('Before applying {}:','After:')

The returning comparator is a function with syntax comparator(target, func, *args, *kwargs)target is a sympy object, func is a sympy function we want to apply, and args,kwargs is the additional positional arguments and keyword arguments pass to func.

In [11]:
from helper import comparator_method_factory
method_comparator = comparator_method_factory('Before calling {}:','After:')

The returning comparator is a function with syntax comparator(target, method_name, *args, *kwargs)target is a sympy object, method_name is a method name we want to call on the object, and args,kwargs is the additional positional arguments and keyword arguments pass to the method.

In [12]:
from helper import comparator_eval_factory

eval_comparator = comparator_eval_factory('Before evaluation:','After:')

comparator returned by comparator_eval_factory has the syntax of comparator(uneval)uneval is the unevaluated Sympy object.

What is symbolic computation

Sympy deals with mathmetical computation symbolicly and mathmethical expressions with unevaluated variables are left in their symbolic form. Let's take an example.

First, we import python built-in functions to compute square root.We might do something like this

In [13]:
from math import sqrt
sqrt(8)
Out[13]:
$\displaystyle 2.8284271247461903$

That is not an accurate represetation of $\sqrt{8}$ and we would have a hard time deducing it from the long float to origin expression. This is symbolic computation comes in. With symbolic computaion system like Sympy, squared roots of numbers that are not perfect square are left unevaluated by default.

In [14]:
from sympy import sqrt
sqrt(8)
Out[14]:
$\displaystyle 2 \sqrt{2}$

Setting up Pretty Printing

In example above the result is great but the printing in jupyter doesn't looks great. To get a prettier printing, call sympy.init_printing()

In [15]:
from sympy import init_printing
init_printing()
In [16]:
sqrt(8)
Out[16]:
$\displaystyle 2 \sqrt{2}$

Looks great now!!

Symbolic computation with variables

Sympy is capable of manipulating expressions with symbolic variables. Here is an example.

In [17]:
from sympy import symbols
x, y = symbols('x y')
expr = x + 2*y
expr
Out[17]:
$\displaystyle x + 2 y$

Obvious simplications are automatically applied by Sympy so in the example below we get $y$ insted of $x+2y-x-y$

In [18]:
expr-x-y
Out[18]:
$\displaystyle y$

That's what we cannot realize without the help from a symbolic computation system like Sympy since in common situations a programming language cannot manipulate a variable before assigning a concret value to it.

Power of Sympy

Here is a small sampling of the sort of symbolic power Sympy is capable of, to whet you appetite. We create several symbol variables first.

In [19]:
x, t, z, nu = symbols('x t z nu')

derivative

Take the derivative of $\sin{(x)}e^x$

In [20]:
s = Derivative(sin(x)*exp(x),x)

eval_comparator(s)
Before evaluation:
$\displaystyle \frac{d}{d x} e^{x} \sin{\left(x \right)}$
After:
$\displaystyle e^{x} \sin{\left(x \right)} + e^{x} \cos{\left(x \right)}$

integral

Compute $\int(e^x\sin{(x)} + e^x\cos{(x)})\,dx$

In [21]:
s = Integral(exp(x)*sin(x) + exp(x)*cos(x))

eval_comparator(s)
Before evaluation:
$\displaystyle \int \left(e^{x} \sin{\left(x \right)} + e^{x} \cos{\left(x \right)}\right)\, dx$
After:
$\displaystyle e^{x} \sin{\left(x \right)}$

Compute $\int_{-\infty}^\infty \sin{(x^2)}\,dx$

In [22]:
s = Integral(sin(x**2),(x,-oo,oo))

eval_comparator(s)
Before evaluation:
$\displaystyle \int\limits_{-\infty}^{\infty} \sin{\left(x^{2} \right)}\, dx$
After:
$\displaystyle \frac{\sqrt{2} \sqrt{\pi}}{2}$

Compute $\lim_{x\to 0}\frac{\sin{(x)}}{x}$

Limit

In [23]:
s = Limit(sin(x)/x, x, 0)

eval_comparator(s)
Before evaluation:
$\displaystyle \lim_{x \to 0^+}\left(\frac{\sin{\left(x \right)}}{x}\right)$
After:
$\displaystyle 1$

Solve $x^2 - 2 = 0$

Solve equation

In [25]:
s = Eq(x**2 - 2, 0)

func_comparator(s, solve, x)
Before applying solve():
$\displaystyle x^{2} - 2 = 0$
After:
$\displaystyle \left[ - \sqrt{2}, \ \sqrt{2}\right]$

Solve differential equation

Solve differential equation $y'' - y = e^t$

In [27]:
y = Function('y')

s = Eq(y(t).diff(t, t) - y(t), exp(t))

func_comparator(s, dsolve, y(t))
Before applying dsolve():
$\displaystyle - y{\left(t \right)} + \frac{d^{2}}{d t^{2}} y{\left(t \right)} = e^{t}$
After:
$\displaystyle y{\left(t \right)} = C_{2} e^{- t} + \left(C_{1} + \frac{t}{2}\right) e^{t}$

Matrix computation

Find the eigenvalue of $\left[\begin{smallmatrix}1 & 2\\2 & 2\end{smallmatrix}\right]$

In [28]:
Matrix([[1, 2], [2, 2]]).eigenvals()
Out[28]:
$\displaystyle \left\{ \frac{3}{2} - \frac{\sqrt{17}}{2} : 1, \ \frac{3}{2} + \frac{\sqrt{17}}{2} : 1\right\}$

Plotting

Plot functions

In [30]:
expr_1 = x**2
range_1 = (x,-2,2)

expr_2 = x
range_2 = (x,-1,1)

p = plot(
    (expr_1,range_1),
    (expr_2,range_2),
    show = False,
    legend = True
);

p[0].line_color = 'r'
p[1].line_color = 'b'

p[0].label = 'Line 1'
p[1].label = 'Line 2'

p.show()

plot 3d surface

In [31]:
from sympy.plotting import plot3d

x,y = symbols('x y')

plot3d(
    (x**2 + y**2, (x, -5, 5), (y, -5, 5)),
    (x*y, (x, -3, 3), (y, -3, 3))
);

Define Symbols

Define variable symbols

Use symbols() to define multiple variables at one. The desired symbols are pass in as string, each symbol seperated by blank.

In [32]:
from sympy import symbols
In [33]:
x,y,z = symbols('x y z')
display(x,y,z)
$\displaystyle x$
$\displaystyle y$
$\displaystyle z$

You can use words instead of characters as symbol names.

In [34]:
speed,time = symbols('speed time')
display(speed,time)
$\displaystyle speed$
$\displaystyle time$

Some strings are reserved for special symbols such as $\lambda, \nu$

In [35]:
lamda, n = symbols('lamda nu')
display(lamda, n)
$\displaystyle \lambda$
$\displaystyle \nu$

Python variable names don't have to keep the same with the symbol names.

In [36]:
y, x = symbols('x y')
display(x, y)
$\displaystyle y$
$\displaystyle x$

But to avoid uncessary confusion, keeping python variables the same with symbol names is recommended.

Define function symbols

Pass cls = Function to define function symbol variales.

In [37]:
f, g = symbols('f g', cls=Function)

display(f(x))

display(g(x,y))
$\displaystyle f{\left(y \right)}$
$\displaystyle g{\left(y,x \right)}$

Define numbers

Use Integer, Float, Rational to define integer, float and rational numbers in Sympy.

In [38]:
from sympy import Integer, Float, Rational
In [39]:
i = Integer(1)

i
Out[39]:
$\displaystyle 1$
In [40]:
f = Float(2.31)

f
Out[40]:
$\displaystyle 2.31$
In [41]:
r = Rational(2,7)

r
Out[41]:
$\displaystyle \frac{2}{7}$

Define expressions

Basic expressions

Just use symbol variables and python operators to construct basic mathmetical expression.

In [42]:
x,y,z = symbols('x y z')

expr = (x+y)**z/(x+1-y/5)

expr
Out[42]:
$\displaystyle \frac{\left(x + y\right)^{z}}{x - \frac{y}{5} + 1}$

When python objects are combined with sympy objects, python objects are converted automatically into Sympy objects. So in most situations wen can use build-in python numbers.

However, Python apply the division of two python numbers first and convert the rational number into a float.

In [43]:
expr = x+1/2

expr
Out[43]:
$\displaystyle x + 0.5$

So when rational number is needed, define the rational number explicitly.

In [44]:
expr = x+Rational(1,2)

expr
Out[44]:
$\displaystyle x + \frac{1}{2}$

More complex expressions like calculuas are constructed with the help of Sympy functions which will be covered in later notes.

Define Equation

Use Eq to define an equation.

In [45]:
x,y = symbols('x,y')

eq = Eq(x**2-x,0)

print('等式n:')
display(eq)
等式n:
$\displaystyle x^{2} - x = 0$

Expression Manipulation

Polynomial/Rational Function

simplify()

Sympy has dozens of function to perform various kinds of expression simplification. simplify() is a general function which attempts to apply all these functions in an intelligent way to arrive at the simplest form of the expression.

Here are some examples.

In [46]:
expr = sin(x)**2 + cos(x)**2
func_comparator(expr, simplify)
Before applying simplify():
$\displaystyle \sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$
After:
$\displaystyle 1$
In [47]:
expr = (x**3 + x**2 - x - 1)/(x**2 + 2*x + 1)

func_comparator(expr, simplify)
Before applying simplify():
$\displaystyle \frac{x^{3} + x^{2} - x - 1}{x^{2} + 2 x + 1}$
After:
$\displaystyle x - 1$
In [48]:
expr = gamma(x)/gamma(x - 2)

func_comparator(expr, simplify)
Before applying simplify():
$\displaystyle \frac{\Gamma\left(x\right)}{\Gamma\left(x - 2\right)}$
After:
$\displaystyle \left(x - 2\right) \left(x - 1\right)$

But 'simplest' form is not well-defined so you don't always get what you want.

In [49]:
expr = x**2 + 2*x + 1

func_comparator(expr, simplify)
Before applying simplify():
$\displaystyle x^{2} + 2 x + 1$
After:
$\displaystyle x^{2} + 2 x + 1$

In the example above, may be $(x+1)^2$ is the simplest form for you but simplify() doesn't agree. In theses situations, we can use more specific simplification functions which we will discuss later to better control the return.

Also, simplify() can be slow since it trys all kinds of simplifications before choose the best one. So you already know what simplification you are after, use the more specific function.

expand()

Given a polymonial, expand() will put it into canonical form of a sum of monomials.

In [50]:
from sympy import expand

expr = (x + 1)**2

func_comparator(expr,expand)
Before applying expand():
$\displaystyle \left(x + 1\right)^{2}$
After:
$\displaystyle x^{2} + 2 x + 1$
In [51]:
expr = (x + 1)*(x - 2) - (x - 1)*x
func_comparator(expr,expand)
Before applying expand():
$\displaystyle - x \left(x - 1\right) + \left(x - 2\right) \left(x + 1\right)$
After:
$\displaystyle -2$

factor()

factor() factor the expression into irreducible factors over the rational number.

In [52]:
from sympy import factor

expr = (x**2)*z + 4*x*y*z + 4*y**2*z

func_comparator(expr, factor)
Before applying factor():
$\displaystyle x^{2} z + 4 x y z + 4 y^{2} z$
After:
$\displaystyle z \left(x + 2 y\right)^{2}$

factor_list()

factor_list() does the same simplification with factor() but return a list of irreducible factors.

In [53]:
from sympy import factor_list

expr = x**2*z + 4*x*y*z + 4*y**2*z

func_comparator(expr, factor_list)
Before applying factor_list():
$\displaystyle x^{2} z + 4 x y z + 4 y^{2} z$
After:
$\displaystyle \left( 1, \ \left[ \left( z, \ 1\right), \ \left( x + 2 y, \ 2\right)\right]\right)$

collect()

collect() collect the common power of a term in the expression.

In [54]:
from sympy import collect

expr = x*y + x - 3 + 2*x**2 - z*x**2 + x**3

func_comparator(expr, collect, x)
Before applying collect():
$\displaystyle x^{3} - x^{2} z + 2 x^{2} + x y + x - 3$
After:
$\displaystyle x^{3} + x^{2} \left(2 - z\right) + x \left(y + 1\right) - 3$

cancel()

cancel() takes any rational function and put it into the canonical form $p/q$, where

  • $p$ and $q$ are expanded polymonials which have no common factors
  • the leading coefficients of $p$ and $q$ have no denominators
In [55]:
from sympy import cancel

expr = (x*y**2 - 2*x*y*z + x*z**2 + y**2 - 2*y*z + z**2)/(x**2 - 1)
                                                 
func_comparator(expr, cancel)
Before applying cancel():
$\displaystyle \frac{x y^{2} - 2 x y z + x z^{2} + y^{2} - 2 y z + z^{2}}{x^{2} - 1}$
After:
$\displaystyle \frac{y^{2} - 2 y z + z^{2}}{x - 1}$

apart

apart() performs a partial fraction decomposition over a rational function, which expresses the function with a sum of polymonials and one or several fractions with simplier donominator.

In [56]:
from sympy import apart

expr = (4*x**3 + 21*x**2 + 10*x + 12)/(x**4 + 5*x**3 + 5*x**2 + 4*x)

func_comparator(expr, apart)
Before applying apart():
$\displaystyle \frac{4 x^{3} + 21 x^{2} + 10 x + 12}{x^{4} + 5 x^{3} + 5 x^{2} + 4 x}$
After:
$\displaystyle \frac{2 x - 1}{x^{2} + x + 1} - \frac{1}{x + 4} + \frac{3}{x}$

Trigonometric simplification

trigsimp

To simplify expressions with trigonometric identities, use trigsimp(). Much like simplify(), trigsimp() attemps to apply various trigonometric identities to the input expression and user heuristics to choose the 'best' one.

In [57]:
from sympy import trigsimp

expr = sin(x)**4 - 2*cos(x)**2*sin(x)**2 + cos(x)**4

func_comparator(expr, trigsimp)
Before applying trigsimp():
$\displaystyle \sin^{4}{\left(x \right)} - 2 \sin^{2}{\left(x \right)} \cos^{2}{\left(x \right)} + \cos^{4}{\left(x \right)}$
After:
$\displaystyle \frac{\cos{\left(4 x \right)}}{2} + \frac{1}{2}$

trigsimp() also works with hyperbolic trig functions.

In [58]:
expr = cosh(x)**2 + sinh(x)**2

func_comparator(expr, trigsimp)
Before applying trigsimp():
$\displaystyle \sinh^{2}{\left(x \right)} + \cosh^{2}{\left(x \right)}$
After:
$\displaystyle \cosh{\left(2 x \right)}$

expand_trig

To expand the trigometric functions, that is, to apply the sum or double angle identities, user expand_trig()

In [59]:
from sympy import expand_trig

expr = sin(x + y)

func_comparator(expr,expand_trig)
Before applying expand_trig():
$\displaystyle \sin{\left(x + y \right)}$
After:
$\displaystyle \sin{\left(x \right)} \cos{\left(y \right)} + \sin{\left(y \right)} \cos{\left(x \right)}$

Power simplification

Assumptions

Before introducing the power simplification functions, a discussion about the identities held by power is in order.

Here we have three kinds of identities

  1. $x^ax^b = x^{a + b}$
  2. $x^ay^a = (xy)^a$
  3. $(x^a)^b = x^{ab}$

Identity 1 is always True.

Identity 2 is not always True. Let's take a counter example for identity 2.

If $x=y=−1$ and $a=1/2$, then $x^ay^a = \sqrt{-1}\sqrt{-1} = i\cdot i = -1$, whereas $x^ay^a = \sqrt{-1}\sqrt{-1} = i\cdot i = -1$.

Identity 3 is not always true either. For example, if $x=−1$, $a=2$, and $b=1/2$, then $(x^a)^b = {\left ((-1)^2\right )}^{1/2} = \sqrt{1} = 1$ and $x^{ab} = (-1)^{2\cdot1/2} = (-1)^1 = -1$

This is important to remember, because by default, sympy will not perform simplification by an identity if it is not True in general.

But if we add additional assumptions, identity 2 and 3 can be always True under these assumptions.

A set of sufficent conditions for identity 2 can be $x, y \geq 0$ and $a \in \mathbb{R}$ and $b \in \mathbb{Z}$ for identity 3.

In order to make Sympy perform simpilications involving identites that are only True under certain assumptions, we need put assumptions on our symbols. (By default, symbols are assumed to complex.)

We will undertake a more detailed discussion later about assumption system and we just take a simple usage below, which assuming the $x,y$ positive and $a,b$ real.

In [60]:
x, y = symbols('x y', positive=True)
a, b = symbols('a b', real=True)

Another way to force the simplification take place regardless of the assumptions is to pass force = True which we will see later.

powsimp

powsimp() apply identity 1 and 2 from left to right.

In [61]:
from sympy import powsimp
expr = x**a*x**b
func_comparator(expr, powsimp)
Before applying powsimp():
$\displaystyle x^{a} x^{b}$
After:
$\displaystyle x^{a + b}$
In [62]:
from sympy import powsimp
expr = x**a*y**a
func_comparator(expr, powsimp)
Before applying powsimp():
$\displaystyle x^{a} y^{a}$
After:
$\displaystyle \left(x y\right)^{a}$

Without necessary assumptions to make identity 2 hold, simplification above doesn't happen.

In [63]:
x, y = symbols('x y')
a, b = symbols('a b')

from sympy import powsimp

expr = x**a*y**a
func_comparator(expr, powsimp)
Before applying powsimp():
$\displaystyle x^{a} y^{a}$
After:
$\displaystyle x^{a} y^{a}$

If you know you want to apply this simplification regardless of the assumptions, you can pass force=True

In [64]:
x, y = symbols('x y')
a, b = symbols('a b')

from sympy import powsimp
expr = x**a*y**a
func_comparator(expr, powsimp,force=True)
Before applying powsimp():
$\displaystyle x^{a} y^{a}$
After:
$\displaystyle \left(x y\right)^{a}$

expand_power_exp

expand_power_exp() applys identity 1 from right to left.

In [65]:
from sympy import expand_power_exp
expr = x**(a + b)
func_comparator(expr, expand_power_exp)
Before applying expand_power_exp():
$\displaystyle x^{a + b}$
After:
$\displaystyle x^{a} x^{b}$

expand_power_base

expand_power_base() applys identity 2 from right to left.

In [66]:
from sympy import expand_power_base
expr = (x*y)**a
func_comparator(expr, expand_power_base)
Before applying expand_power_base():
$\displaystyle \left(x y\right)^{a}$
After:
$\displaystyle \left(x y\right)^{a}$

powdenest

powdenest() apply the idenity 3 from left to right.

In [67]:
from sympy import powdenest
expr = (x**a)**b
func_comparator(expr, powdenest,force=True)
Before applying powdenest():
$\displaystyle \left(x^{a}\right)^{b}$
After:
$\displaystyle x^{a b}$

Exponentials and logarithms

There are two primary identities for logarithms.

  1. $\log{(xy)} = \log{(x)} + \log{(y)}$
  2. $\log{(x^n)} = n\log{(x)}$

They have similiar issues with powers. To make simplification honor the identites, we shoud pass force = True or put additional assumptions.

One set of sufficient assumptions is

In [68]:
x, y = symbols('x y', positive=True)
n = symbols('n', real=True)

expand_log

expand_log() applys identity 1 and 2 from left to right

In [69]:
from sympy import expand_log

expr = log(x*y)

func_comparator(expr,expand_log)
Before applying expand_log():
$\displaystyle \log{\left(x y \right)}$
After:
$\displaystyle \log{\left(x \right)} + \log{\left(y \right)}$
In [70]:
from sympy import expand_log

expr = log(x**n)

func_comparator(expr,expand_log)
Before applying expand_log():
$\displaystyle \log{\left(x^{n} \right)}$
After:
$\displaystyle n \log{\left(x \right)}$
In [71]:
from sympy import expand_log

expr = log(x/y)

func_comparator(expr,expand_log)
Before applying expand_log():
$\displaystyle \log{\left(\frac{x}{y} \right)}$
After:
$\displaystyle \log{\left(x \right)} - \log{\left(y \right)}$

logcombine

expand_log() applys identity 1 and 2 from right to left

In [72]:
from sympy import logcombine

expr = log(x) + log(y)

func_comparator(expr, logcombine)
Before applying logcombine():
$\displaystyle \log{\left(x \right)} + \log{\left(y \right)}$
After:
$\displaystyle \log{\left(x y \right)}$
In [73]:
from sympy import logcombine

expr = n*log(x)

func_comparator(expr, logcombine)
Before applying logcombine():
$\displaystyle n \log{\left(x \right)}$
After:
$\displaystyle \log{\left(x^{n} \right)}$

Combitorial

combsimp

To simplify combitorial expressions, use combsimp()

In [74]:
from sympy import combsimp

expr = factorial(n)/factorial(n - 3)

func_comparator(expr, combsimp)
Before applying combsimp():
$\displaystyle \frac{n!}{\left(n - 3\right)!}$
After:
$\displaystyle n \left(n - 2\right) \left(n - 1\right)$
In [75]:
from sympy import combsimp, binomial

n,k = symbols('n k')

expr = binomial(n+1, k+1)/binomial(n, k)

func_comparator(expr, combsimp)
Before applying combsimp():
$\displaystyle \frac{{\binom{n + 1}{k + 1}}}{{\binom{n}{k}}}$
After:
$\displaystyle \frac{n + 1}{k + 1}$