• SSC Lunch Time Python
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Lunch Time Python¶

Lunch 2: SymPy¶

No description has been provided for this image

SymPy is a Python library for symbolic mathematics. It can parse mathematical expressions, substitute, differentiate, integrate and evaluate them, as well as solve algebraic and differential equations. It is also itself written entirely in Python, with a focus keeping the code comprehensible and easily extensible. There is also a related project SymEngine, which is written in C++ with a focus on speed, which offers a much faster implementation of a subset of SymPy's functionality.

Press Spacebar to go to the next slide (or ? to see all navigation shortcuts)

Lunch Time Python, Scientific Software Center, Heidelberg University

SymPy Installation¶

  • Anaconda: pre-installed
  • Conda: conda install sympy
  • Pip: python -m pip install sympy

Or try it out online:

  • live.sympy.org
    • online python shell with SymPy installed
  • sympygamma.com
    • SymPy powered web app similar to Wolfram|Alpha
  • this notebook on binder
    • interactive version of this notebook on binder

Symbols¶

  • basic building block of expressions
  • sympy.symbols
    • takes a string of variable names separated by spaces
    • returns a tuple of Symbol objects, one for each name
    • usually a good idea to use the same name for the Python object, but you don't have to
In [1]:
import sympy as sp

# Define a single symbol x named 'x':

x = sp.symbols("x")
In [2]:
x
Out[2]:
$\displaystyle x$
In [3]:
type(x)
Out[3]:
sympy.core.symbol.Symbol
In [4]:
# Define multiple symbols at once:

y, z, t, nu = sp.symbols("y z t nu")
In [5]:
# Can use different name for symbol & object (not recommended!)

confusing = sp.symbols("alpha")
print(confusing)
alpha

Expressions¶

  • Symbols can be combined with the usual arithmetic operations (+, -, *, /)
  • Result is an expression
  • The type of an expression depends on the operation
In [6]:
expr = x + y - 2 * z
In [7]:
expr
Out[7]:
$\displaystyle x + y - 2 z$
In [8]:
type(expr)
Out[8]:
sympy.core.add.Add
In [9]:
expr.args
Out[9]:
(x, y, -2*z)
In [10]:
type(expr.args[2])
Out[10]:
sympy.core.mul.Mul
In [11]:
expr.args[2].args
Out[11]:
(-2, z)

Numbers¶

  • will automatically convert to SymPy Numbers when required
  • but take care with math only involving Python numbers!
  • sometimes an explicit conversion is helpful
In [12]:
x + 1
Out[12]:
$\displaystyle x + 1$
In [13]:
type((1 + x).args[0])
Out[13]:
sympy.core.numbers.One
In [14]:
type((123 + x).args[0])
Out[14]:
sympy.core.numbers.Integer
In [15]:
# be careful with unexpected Python maths!
x + 1 / 2
Out[15]:
$\displaystyle x + 0.5$
In [16]:
# explicit construction of Rational
x + sp.Rational(1, 2)
Out[16]:
$\displaystyle x + \frac{1}{2}$
In [17]:
# explicit construction of Integer
x + sp.Integer(1) / 2
Out[17]:
$\displaystyle x + \frac{1}{2}$
In [18]:
# equivalent but now all operations involve a sympy expr:
(2 * x + 1) / 2
Out[18]:
$\displaystyle x + \frac{1}{2}$

Functions¶

  • sympy.functions contains many built-in analytic functions
In [19]:
expr = sp.sin(x) * sp.exp(y) + sp.gamma(t)
In [20]:
expr
Out[20]:
$\displaystyle e^{y} \sin{\left(x \right)} + \Gamma\left(t\right)$

Substitution¶

  • evaluate an expression by substituting numbers for symbols
  • substitute sub-expressions for sub-expressions
  • .subs takes a list of (old, new) pairs
In [21]:
(x + 1).subs(x, 1)
Out[21]:
$\displaystyle 2$
In [22]:
(x * sp.cos(y)).subs([(x, 1), (y, 0.2)])
Out[22]:
$\displaystyle 0.980066577841242$
In [23]:
(x * y).subs(x, y**3)
Out[23]:
$\displaystyle y^{4}$

Parsing¶

  • sympify converts a string into a SymPy expression
  • existing symbols are used if the name matches
  • otherwise new symbols will be created as necessary
In [24]:
expr = sp.sympify("x")
In [25]:
type(expr)
Out[25]:
sympy.core.symbol.Symbol
In [26]:
expr == x
Out[26]:
True
In [27]:
expr = sp.sympify("cos(x) + a")
In [28]:
expr
Out[28]:
$\displaystyle a + \cos{\left(x \right)}$
In [29]:
expr.args[0]
Out[29]:
$\displaystyle a$
In [30]:
expr.args[1].args[0] == x
Out[30]:
True

Evaluating¶

  • evalf for one off numerical evaluation of an expression
    • can take a dict of Symbol : number pairs
  • lambdify for efficient repeated evaluation of an expression
    • replaces sympy functions like sin, cos with numpy equivalents
In [31]:
sp.sqrt(2)
Out[31]:
$\displaystyle \sqrt{2}$
In [32]:
sp.sqrt(2).evalf()
Out[32]:
$\displaystyle 1.4142135623731$
In [33]:
expr = sp.sqrt(1 + x)
In [34]:
expr
Out[34]:
$\displaystyle \sqrt{x + 1}$
In [35]:
expr.evalf(subs={x: 1})
Out[35]:
$\displaystyle 1.4142135623731$
In [36]:
f = sp.lambdify(x, expr)
In [37]:
f(1)
Out[37]:
1.4142135623730951
In [38]:
import numpy as np

a = np.array([1, 2, 3, 5.21])
f(a)
Out[38]:
array([1.41421356, 1.73205081, 2.        , 2.49198716])

Simplification¶

  • simplify applies various simplifications to an expression
  • expand expands a polynomial to a sum of monomials
  • factor looks for common terms to factorize a polynomial
  • collect collects common powers of a term
  • as well as many more
In [39]:
sp.cos(x) ** 2 + sp.sin(x) ** 2
Out[39]:
$\displaystyle \sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$
In [40]:
sp.simplify(sp.cos(x) ** 2 + sp.sin(x) ** 2)
Out[40]:
$\displaystyle 1$
In [41]:
sp.gamma(x) / sp.gamma(x - 3)
Out[41]:
$\displaystyle \frac{\Gamma\left(x\right)}{\Gamma\left(x - 3\right)}$
In [42]:
sp.simplify(sp.gamma(x) / sp.gamma(x - 3))
Out[42]:
$\displaystyle \left(x - 3\right) \left(x - 2\right) \left(x - 1\right)$
In [43]:
(z + t) ** 2 * (x + 2 * y) ** 6
Out[43]:
$\displaystyle \left(t + z\right)^{2} \left(x + 2 y\right)^{6}$
In [44]:
print(sp.expand((z + t) ** 2 * (x + 2 * y) ** 6))
t**2*x**6 + 12*t**2*x**5*y + 60*t**2*x**4*y**2 + 160*t**2*x**3*y**3 + 240*t**2*x**2*y**4 + 192*t**2*x*y**5 + 64*t**2*y**6 + 2*t*x**6*z + 24*t*x**5*y*z + 120*t*x**4*y**2*z + 320*t*x**3*y**3*z + 480*t*x**2*y**4*z + 384*t*x*y**5*z + 128*t*y**6*z + x**6*z**2 + 12*x**5*y*z**2 + 60*x**4*y**2*z**2 + 160*x**3*y**3*z**2 + 240*x**2*y**4*z**2 + 192*x*y**5*z**2 + 64*y**6*z**2
In [45]:
sp.factor(
    t**2 * x**6
    + 12 * t**2 * x**5 * y
    + 60 * t**2 * x**4 * y**2
    + 160 * t**2 * x**3 * y**3
    + 240 * t**2 * x**2 * y**4
    + 192 * t**2 * x * y**5
    + 64 * t**2 * y**6
    + 2 * t * x**6 * z
    + 24 * t * x**5 * y * z
    + 120 * t * x**4 * y**2 * z
    + 320 * t * x**3 * y**3 * z
    + 480 * t * x**2 * y**4 * z
    + 384 * t * x * y**5 * z
    + 128 * t * y**6 * z
    + x**6 * z**2
    + 12 * x**5 * y * z**2
    + 60 * x**4 * y**2 * z**2
    + 160 * x**3 * y**3 * z**2
    + 240 * x**2 * y**4 * z**2
    + 192 * x * y**5 * z**2
    + 64 * y**6 * z**2
)
Out[45]:
$\displaystyle \left(t + z\right)^{2} \left(x + 2 y\right)^{6}$

Differentiation¶

  • sympy.diff differentiates expressions
    • diff(f, x) : $df/dx$
    • diff(f, x, 3) : $d^3f/dx^3$
    • diff(f, x, y, z) : $d^3f/dxdydz$
In [46]:
expr
Out[46]:
$\displaystyle \sqrt{x + 1}$
In [47]:
sp.diff(expr, x)
Out[47]:
$\displaystyle \frac{1}{2 \sqrt{x + 1}}$

Integration¶

  • sympy.integrate integrates expressions
    • indefinite by default, without the integration constant
    • definite if tuple of (Symbol, lower_limit, upper_limit) is provided
In [48]:
expr
Out[48]:
$\displaystyle \sqrt{x + 1}$
In [49]:
sp.integrate(expr, x)
Out[49]:
$\displaystyle \frac{2 \left(x + 1\right)^{\frac{3}{2}}}{3}$
In [50]:
sp.integrate(expr, (x, 0, 1))
Out[50]:
$\displaystyle - \frac{2}{3} + \frac{4 \sqrt{2}}{3}$
In [51]:
sp.diff(sp.integrate(expr, x), x)
Out[51]:
$\displaystyle \sqrt{x + 1}$

Limits¶

  • sympy.limit takes limit of expression at singular point
In [52]:
sinc = sp.sin(x) / x
In [53]:
sinc.subs(x, 0)
Out[53]:
$\displaystyle \text{NaN}$
In [54]:
sinc.limit(x, 0)
Out[54]:
$\displaystyle 1$

Series expansion¶

  • sympy.series expands an expression around a point
In [55]:
sinc.series(x, 0, 10)
Out[55]:
$\displaystyle 1 - \frac{x^{2}}{6} + \frac{x^{4}}{120} - \frac{x^{6}}{5040} + \frac{x^{8}}{362880} + O\left(x^{10}\right)$

SymEngine¶

  • Fast C++ implementation of (some of) SymPy
In [56]:
expr = sp.cos(x + 7 * x**2) + sp.sin(x - 2 * x**3)
In [57]:
%time _ = (sp.series(expr, n=100))
CPU times: user 3.7 s, sys: 6.77 ms, total: 3.71 s
Wall time: 3.7 s
In [58]:
if "google.colab" in str(get_ipython()):
    !pip install symengine -qqq
import symengine as se

%time _ = se.series(expr, n=100)
CPU times: user 658 μs, sys: 0 ns, total: 658 μs
Wall time: 663 μs

More at docs.sympy.org¶

  • Algebraic equation solving
  • Differential equation solving
  • Matrices
  • Assumptions
  • Printing
  • Code generation

ssciwr.github.io/lunch-time-python¶