Lunch Time Python¶
Lunch 2: SymPy¶
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
- can take a dict of
lambdify
for efficient repeated evaluation of an expression- replaces sympy functions like
sin
,cos
with numpy equivalents
- replaces sympy functions like
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])
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 expressionsdiff(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.77 s, sys: 20.6 ms, total: 3.79 s Wall time: 3.79 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 559 μs, sys: 49 μs, total: 608 μs Wall time: 613 μs
More at docs.sympy.org¶
- Algebraic equation solving
- Differential equation solving
- Matrices
- Assumptions
- Printing
- Code generation