# Lunch Time Python

## Lunch 2: SymPy



[SymPy](https://www.sympy.org/) 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](https://github.com/symengine/symengine.py),
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](https://ssciwr.github.io/lunch-time-python/), [Scientific Software Center](https://ssc.iwr.uni-heidelberg.de), [Heidelberg University](https://www.uni-heidelberg.de/)

# SymPy Installation

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

Or try it out online:

- [live.sympy.org](https://live.sympy.org/)
  - online python shell with SymPy installed
- [sympygamma.com](https://www.sympygamma.com/input/?i=cos%28x%29)
  - SymPy powered web app similar to [Wolfram|Alpha](https://www.wolframalpha.com/)
- [this notebook on binder](https://mybinder.org/v2/gh/ssciwr/lunch-time-python.git/HEAD?labpath=lunchtime2%2Flunchtime2.ipynb)
  - 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


```python
import sympy as sp

# Define a single symbol x named 'x':

x = sp.symbols("x")
```


```python
x
```


```python
type(x)
```


```python
# Define multiple symbols at once:

y, z, t, nu = sp.symbols("y z t nu")
```


```python
# Can use different name for symbol & object (not recommended!)

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

# Expressions

- Symbols can be combined with the usual arithmetic operations (`+`, `-`, `*`, `/`)
- Result is an expression
- The type of an expression depends on the operation


```python
expr = x + y - 2 * z
```


```python
expr
```


```python
type(expr)
```


```python
expr.args
```


```python
type(expr.args[2])
```


```python
expr.args[2].args
```

# Numbers

- will automatically convert to SymPy Numbers when required
- but take care with math only involving Python numbers!
- sometimes an explicit conversion is helpful


```python
x + 1
```


```python
type((1 + x).args[0])
```


```python
type((123 + x).args[0])
```


```python
# be careful with unexpected Python maths!
x + 1 / 2
```


```python
# explicit construction of Rational
x + sp.Rational(1, 2)
```


```python
# explicit construction of Integer
x + sp.Integer(1) / 2
```


```python
# equivalent but now all operations involve a sympy expr:
(2 * x + 1) / 2
```

# Functions

- `sympy.functions` contains many built-in analytic functions


```python
expr = sp.sin(x) * sp.exp(y) + sp.gamma(t)
```


```python
expr
```

# Substitution

- evaluate an expression by substituting numbers for symbols
- substitute sub-expressions for sub-expressions
- `.subs` takes a list of `(old, new)` pairs


```python
(x + 1).subs(x, 1)
```


```python
(x * sp.cos(y)).subs([(x, 1), (y, 0.2)])
```


```python
(x * y).subs(x, y ** 3)
```

# 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


```python
expr = sp.sympify("x")
```


```python
type(expr)
```


```python
expr == x
```


```python
expr = sp.sympify("cos(x) + a")
```


```python
expr
```


```python
expr.args[0]
```


```python
expr.args[1].args[0] == x
```

# 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


```python
sp.sqrt(2)
```


```python
sp.sqrt(2).evalf()
```


```python
expr = sp.sqrt(1 + x)
```


```python
expr
```


```python
expr.evalf(subs={x: 1})
```


```python
f = sp.lambdify(x, expr)
```


```python
f(1)
```


```python
import numpy as np

a = np.array([1, 2, 3, 5.21])
f(a)
```

# 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](https://docs.sympy.org/latest/tutorial/simplification.html)



```python
sp.cos(x) ** 2 + sp.sin(x) ** 2
```


```python
sp.simplify(sp.cos(x) ** 2 + sp.sin(x) ** 2)
```


```python
sp.gamma(x) / sp.gamma(x - 3)
```


```python
sp.simplify(sp.gamma(x) / sp.gamma(x - 3))
```


```python
(z + t) ** 2 * (x + 2 * y) ** 6
```


```python
print(sp.expand((z + t) ** 2 * (x + 2 * y) ** 6))
```


```python
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
)
```

# 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$


```python
expr
```


```python
sp.diff(expr, x)
```

# Integration

- `sympy.integrate` integrates expressions
  - indefinite by default, without the integration constant
  - definite if tuple of `(Symbol, lower_limit, upper_limit)` is provided


```python
expr
```


```python
sp.integrate(expr, x)
```


```python
sp.integrate(expr, (x, 0, 1))
```


```python
sp.diff(sp.integrate(expr, x), x)
```

# Limits

- `sympy.limit` takes limit of expression at singular point


```python
sinc = sp.sin(x) / x
```


```python
sinc.subs(x, 0)
```


```python
sinc.limit(x, 0)
```

# Series expansion

- `sympy.series` expands an expression around a point


```python
sinc.series(x, 0, 10)
```

# SymEngine

- Fast C++ implementation of (some of) SymPy


```python
expr = sp.cos(x + 7 * x ** 2) + sp.sin(x - 2 * x ** 3)
```


```python
%time _ = (sp.series(expr, n=100))
```


```python
import symengine as se

%time _ = se.series(expr, n=100)
```

# More at [docs.sympy.org](https://docs.sympy.org/)

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

## [ssciwr.github.io/lunch-time-python](https://ssciwr.github.io/lunch-time-python/)

d40215ecd21afbf19e6a0dc3bbc56352a22065fa
ipynb
Jupyter Notebook
lunchtime2/lunchtime2.ipynb
ssciwr/lunch-time-python
8084127ab87732640cff3c6ea581a9de21f2dfa3
MIT
lunchtime2/lunchtime2.ipynb
ssciwr/lunch-time-python
8084127ab87732640cff3c6ea581a9de21f2dfa3
MIT
2022-03-25T11:51:04.000Z
2022-03-25T11:53:54.000Z
lunchtime2/lunchtime2.ipynb
ssciwr/lunch-time-python
8084127ab87732640cff3c6ea581a9de21f2dfa3
MIT
Qwen/Qwen-72B
1. YES
2. YES